Project

General

Profile

Feature #37362 » feature-37362.patch

Mizuki ISHIKAWA, 2022-07-15 03:03

View differences:

app/controllers/reports_controller.rb
21 21
  menu_item :issues
22 22
  before_action :find_project, :authorize, :find_issue_statuses
23 23

  
24
  include ReportsHelper
24 25
  def issue_report
25 26
    with_subprojects = Setting.display_subprojects_issues?
26 27
    @trackers = @project.rolled_up_trackers(with_subprojects).visible
......
82 83
    else
83 84
      render_404
84 85
    end
86
    respond_to do |format|
87
      format.html
88
      format.csv do
89
        send_data(issue_report_details_to_csv(@field, @statuses, @rows, @data), :type => 'text/csv; header=present', :filename => "report-#{params[:detail]}.csv")
90
      end
91
    end
85 92
  end
86 93

  
87 94
  private
app/helpers/reports_helper.rb
44 44
    parameters = {:set_filter => 1, :subproject_id => '!*', field => (row.id || '!*')}.merge(options)
45 45
    project_issues_path(row.is_a?(Project) ? row : project, parameters)
46 46
  end
47

  
48
  def issue_report_details_to_csv(field_name, statuses, rows, data)
49
    Redmine::Export::CSV.generate(:encoding => params[:encoding]) do |csv|
50
      # csv headers
51
      headers = [''] + statuses.map(&:name) + [l(:label_open_issues_plural), l(:label_closed_issues_plural), l(:label_total)]
52
      csv << headers
53

  
54
      # csv lines
55
      rows.each do |row|
56
        csv <<
57
          [row.name] +
58
            statuses.map{|s| aggregate(data, { field_name => row.id, "status_id" => s.id })} +
59
            [aggregate(data, { field_name => row.id, "closed" => 0 })] +
60
            [aggregate(data, { field_name => row.id, "closed" => 1 })] +
61
            [aggregate(data, { field_name => row.id })]
62
      end
63
    end
64
  end
47 65
end
app/views/reports/_details.html.erb
25 25
<% end %>
26 26
</tbody>
27 27
</table>
28
<% other_formats_links do |f| %>
29
  <%= f.link_to_with_query_parameters 'CSV', {}, :onclick => "showModal('csv-export-options', '330px'); return false;" %>
30
<% end %>
31
<div id="csv-export-options" style="display: none;">
32
  <h3 class="title"><%= l(:label_export_options, :export_format => 'CSV') %></h3>
33
  <%= form_tag(project_issues_report_details_path(@project, :detail => params[:detail], :format => 'csv'), :method => :get, :id => 'csv-export-form') do %>
34
  <%= export_csv_encoding_select_tag %>
35
  <p class="buttons">
36
    <%= submit_tag l(:button_export), :name => nil, :onclick => 'hideModal(this);', :data => {:disable_with => false} %>
37
    <%= link_to_function l(:button_cancel), 'hideModal(this);' %>
38
  </p>
39
  <% end %>
40
</div>
41

  
28 42
<div class="issue-report-graph hide-when-print">
29 43
  <canvas id="issues_by_<%= params[:detail] %>"></canvas>
30 44
</div>
test/functional/reports_controller_test.rb
242 242
    )
243 243
    assert_response 404
244 244
  end
245

  
246
  def test_issue_report_details_should_csv_export
247
    %w(tracker version priority category assigned_to author subproject).each do |detail|
248
      get(
249
        :issue_report_details,
250
        params: {
251
          id: 1,
252
          detail: detail,
253
          format: 'csv'
254
        }
255
      )
256
      assert_response :success
257
      assert_equal 'text/csv; header=present', response.media_type
258
    end
259
  end
260

  
261
  def test_issue_report_details_with_tracker_detail_should_csv_export
262
    project = Project.find(1)
263
    tracker = project.trackers.find_by(:name => 'Support request')
264
    project.trackers.delete(tracker)
265

  
266
    with_settings :display_subprojects_issues => '1' do
267
      get(
268
        :issue_report_details,
269
        params: {
270
          id: 1,
271
          detail: 'tracker',
272
          format: 'csv'
273
        }
274
      )
275
      assert_response :success
276

  
277
      assert_equal 'text/csv; header=present', response.media_type
278
      lines = response.body.chomp.split("\n")
279
      # Number of lines
280
      rows = Project.find(1).rolled_up_trackers(true).visible
281
      assert_equal rows.size + 1, lines.size
282
      # Header
283
      assert_equal "\"\",New,Assigned,Resolved,Feedback,Closed,Rejected,open,closed,Total", lines.first
284
      # Details
285
      to_test = [
286
        'Bug,5,0,0,0,3,0,5,3,8',
287
        'Feature request,0,1,0,0,0,0,1,0,1',
288
        'Support request,0,0,0,0,0,0,0,0,0'
289
      ]
290
      to_test.each do |expected|
291
        assert_includes lines, expected
292
      end
293
    end
294
  end
295

  
296
  def test_issue_report_details_with_assigned_to_detail_should_csv_export
297
    Issue.delete_all
298
    Issue.generate!
299
    Issue.generate!
300
    Issue.generate!(:status_id => 5)
301
    Issue.generate!(:assigned_to_id => 2)
302

  
303
    with_settings :issue_group_assignment => '1' do
304
      get(
305
        :issue_report_details,
306
        params: {
307
          id: 1,
308
          detail: 'assigned_to',
309
          format: 'csv'
310
        }
311
      )
312
      assert_response :success
313

  
314
      assert_equal 'text/csv; header=present', response.media_type
315
      lines = response.body.chomp.split("\n")
316
      # Number of lines
317
      rows = Project.find(1).principals.sorted + [I18n.t(:label_none)]
318
      assert_equal rows.size + 1, lines.size
319
      # Header
320
      assert_equal "\"\",New,Assigned,Resolved,Feedback,Closed,Rejected,open,closed,Total", lines.first
321
      # Details
322
      to_test = [
323
        'Dave Lopper,0,0,0,0,0,0,0,0,0',
324
        'John Smith,1,0,0,0,0,0,1,0,1',
325
        '[none] ,2,0,0,0,1,0,2,1,3'
326
      ]
327
      to_test.each do |expected|
328
        assert_includes lines, expected
329
      end
330
    end
331
  end
245 332
end
(2-2/2)