Project

General

Profile

Patch #1671 » version_times_251.diff

P S, 2014-05-15 14:52

View differences:

app/helpers/versions_helper.rb (kopia robocza)
32 32
  def render_issue_status_by(version, criteria)
33 33
    criteria = 'tracker' unless STATUS_BY_CRITERIAS.include?(criteria)
34 34

  
35
    h = Hash.new {|k,v| k[v] = [0, 0]}
36
    begin
37
      # Total issue count
38
      Issue.where(:fixed_version_id => version.id).group(criteria).count.each {|c,s| h[c][0] = s}
39
      # Open issues count
40
      Issue.open.where(:fixed_version_id => version.id).group(criteria).count.each {|c,s| h[c][1] = s}
41
    rescue ActiveRecord::RecordNotFound
42
    # When grouping by an association, Rails throws this exception if there's no result (bug)
35
    #sort them alphabetically by category name
36
    metrics = version.get_grouped_metrics(criteria).to_a.sort {|x, y| x[0].to_s <=> y[0].to_s} 
37
    max = {}
38
    
39
    [{:count => :total}, {:time => :total}].each do |metric_info| 
40
      metrics_group, total_metric = metric_info.to_a.flatten
41
      max[metrics_group] = metrics.map{|item| item[1]}.map {|item| item[metrics_group]}.map {|item| item[total_metric]}.max
42
      max[metrics_group] = 1 if max[metrics_group] == 0
43 43
    end
44 44
    # Sort with nil keys in last position
45
    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])}}
46
    max = counts.collect {|c| c[:total]}.max
47

  
48
    render :partial => 'issue_counts', :locals => {:version => version, :criteria => criteria, :counts => counts, :max => max}
45
    render :partial => 'issue_counts', :locals => {:version => version, 
46
      :criteria => criteria, :grouped_metrics => metrics, :max => max, 
47
      :spent_time_allowed => User.current.allowed_to?(:view_time_entries, @project), 
48
      }
49 49
  end
50
  
51
  def time_progress(time_info)  
52
    logger.debug "time_info[:spent] = #{time_info[:spent].inspect}"
53
    logger.debug "time_info[:total] = #{time_info[:total].inspect}"
54
    if (time_info[:total] != 0)
55
      time_progress = time_info[:spent].to_f / time_info[:total]
56
    else
57
      time_progress = 0 #no total also means there's no spent time
58
    end    
59
    time_progress
60
  end
50 61

  
51 62
  def status_by_options_for_select(value)
52 63
    options_for_select(STATUS_BY_CRITERIAS.collect {|criteria| [l("field_#{criteria}".to_sym), criteria]}, value)
app/models/version.rb (kopia robocza)
82 82
  def spent_hours
83 83
    @spent_hours ||= TimeEntry.joins(:issue).where("#{Issue.table_name}.fixed_version_id = ?", id).sum(:hours).to_f
84 84
  end
85

  
85
   
86
  def calc_remaining_and_total_time
87
    @remaining_hours = 0
88
    @total_hours = 0
89
    get_grouped_metrics(:category).to_a.map{|item| item[1]}.map {|item| item[:time]}.each do |times| 
90
      @remaining_hours += times[:remaining]
91
      @total_hours += times[:total]
92
    end
93
  end
94
  
95
  def remaining_hours
96
    return @remaining_hours if @remaining_hours
97
    calc_remaining_and_total_time
98
    @remaining_hours    
99
  end
100
  
101
  def total_hours
102
    return @total_hours if @total_hours
103
    calc_remaining_and_total_time
104
    @total_hours
105
  end
106
  
86 107
  def closed?
87 108
    status == 'closed'
88 109
  end
......
163 184
    @closed_issues_count
164 185
  end
165 186

  
187
  def not_estimated_undone_count
188
    @not_estimated_undone_count ||= Issue.count(:all, :conditions => 
189
        ["fixed_version_id = ? AND estimated_hours IS NULL AND " + 
190
          "(closed_on is null)", self.id
191
        ], :include => :status)
192
  end
193

  
194

  
166 195
  def wiki_page
167 196
    if project.wiki && !wiki_page_title.blank?
168 197
      @wiki_page ||= project.wiki.find_page(wiki_page_title)
......
226 255
    end
227 256
  end
228 257

  
258
  def get_grouped_metrics(criteria)
259
    condition = issues_version_condition
260
    
261
    issues = Issue.find(:all, :include => [:status, criteria], 
262
      :conditions => condition)
263
    
264
    spent_times = {}
265
    TimeEntry.sum(:hours, :group => :issue_id, :include => :issue,
266
        :conditions => condition).each do |issue_id, hours| 
267
        
268
      spent_times[issue_id] = hours
269
    end
270

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

  
229 304
  private
230

  
231 305
  def load_issue_counts
232 306
    unless @issue_count
233 307
      @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(h(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
                  :width => "#{(count[:total].to_f / max * 200).floor}px;") %>
27
        </td>
28
    </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>
29 96
    <% end %>
30 97
    </table>
31 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_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)
375 375
  setting_enabled_scm: Enabled SCM
376 376
  setting_mail_handler_body_delimiters: "Truncate emails after one of these lines"
377 377
  setting_mail_handler_api_enabled: Enable WS for incoming emails
378
  label_remaining_time: Remaining time
379
  label_current_total_time: Total
380
  label_estimated_time_short: Est.
381
  label_spent_time_short: Spent
382
  label_remaining_time_short: Rem.
383
  label_issues_count: Count
384
  label_not_estimated_and_undone: Not estimated
385
  label_time: Time
378 386
  setting_mail_handler_api_key: API key
379 387
  setting_sequential_project_identifiers: Generate sequential project identifiers
380 388
  setting_gravatar_enabled: Use Gravatar user icons
......
610 618
  label_open_issues_plural: open
611 619
  label_closed_issues: closed
612 620
  label_closed_issues_plural: closed
621
  label_done_issues: done
622
  label_done_issues_plural: done
623
  label_undone_issues: undone
624
  label_undone_issues_plural: undone
613 625
  label_x_open_issues_abbr_on_total:
614 626
    zero:  0 open / %{total}
615 627
    one:   1 open / %{total}
config/locales/pl.yml (kopia robocza)
361 361
  label_closed_issues_plural234: zamknięte
362 362
  label_closed_issues_plural5: zamknięte
363 363
  label_closed_issues_plural: zamknięte
364
  label_done_issues: done
365
  label_done_issues_plural: done
366
  label_undone_issues: undone
367
  label_undone_issues_plural: undone
364 368
  label_x_open_issues_abbr_on_total:
365 369
    zero:  0 otwartych / %{total}
366 370
    one:   1 otwarty / %{total}
......
749 753
  setting_login_required: Wymagane zalogowanie
750 754
  setting_mail_from: Adres e-mail wysyłki
751 755
  setting_mail_handler_api_enabled: Uaktywnij usługi sieciowe (WebServices) dla poczty przychodzącej
756
  label_remaining_time: Pozostały czas
757
  label_current_total_time: Suma
758
  label_estimated_time_short: Szacz.
759
  label_spent_time_short: Przepr.
760
  label_remaining_time_short: Pozost.
761
  label_issues_count: Count
762
  label_time: Czas
763
  label_not_estimated_and_undone: Nie oszacowane
764
  text_hours_short: h
765
  text_not_assigned: Nie przydzielone
752 766
  setting_mail_handler_api_key: Klucz API
753 767
  setting_per_page_options: Opcje ilości obiektów na stronie
754 768
  setting_plain_text_mail: tylko tekst (bez HTML)
public/stylesheets/application.css (kopia robocza)
413 413
div#version-summary fieldset { margin-bottom: 1em; }
414 414
div#version-summary fieldset.time-tracking table { width:100%; }
415 415
div#version-summary th, div#version-summary td.total-hours { text-align: right; }
416
.not_estimated a { color: red; }
416 417

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