summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGo MAEDA <maeda@farend.jp>2022-07-21 04:13:37 +0000
committerGo MAEDA <maeda@farend.jp>2022-07-21 04:13:37 +0000
commit0c037a7b48e061fa32e96c21718b30560431c4c7 (patch)
treefeba0bcd09960e044151cdafd0b69f740b098918
parentcfbc2e7a33cb068f1dd71b06a2625ebeecd56ccb (diff)
downloadredmine-0c037a7b48e061fa32e96c21718b30560431c4c7.tar.gz
redmine-0c037a7b48e061fa32e96c21718b30560431c4c7.zip
CSV export of issues report (#37362).
Patch by Mizuki ISHIKAWA. git-svn-id: https://svn.redmine.org/redmine/trunk@21732 e93f8b46-1217-0410-a6f0-8f06a7374b81
-rw-r--r--app/controllers/reports_controller.rb9
-rw-r--r--app/helpers/reports_helper.rb18
-rw-r--r--app/views/reports/_details.html.erb14
-rw-r--r--test/functional/reports_controller_test.rb87
4 files changed, 128 insertions, 0 deletions
diff --git a/app/controllers/reports_controller.rb b/app/controllers/reports_controller.rb
index d818e0b08..1cac99655 100644
--- a/app/controllers/reports_controller.rb
+++ b/app/controllers/reports_controller.rb
@@ -21,6 +21,7 @@ class ReportsController < ApplicationController
menu_item :issues
before_action :find_project, :authorize, :find_issue_statuses
+ include ReportsHelper
def issue_report
with_subprojects = Setting.display_subprojects_issues?
@trackers = @project.rolled_up_trackers(with_subprojects).visible
@@ -82,6 +83,14 @@ class ReportsController < ApplicationController
else
render_404
end
+ respond_to do |format|
+ format.html
+ format.csv do
+ send_data(issue_report_details_to_csv(@field, @statuses, @rows, @data),
+ :type => 'text/csv; header=present',
+ :filename => "report-#{params[:detail]}.csv")
+ end
+ end
end
private
diff --git a/app/helpers/reports_helper.rb b/app/helpers/reports_helper.rb
index a01abf3a3..36ee6ddce 100644
--- a/app/helpers/reports_helper.rb
+++ b/app/helpers/reports_helper.rb
@@ -44,4 +44,22 @@ module ReportsHelper
parameters = {:set_filter => 1, :subproject_id => '!*', field => (row.id || '!*')}.merge(options)
project_issues_path(row.is_a?(Project) ? row : project, parameters)
end
+
+ def issue_report_details_to_csv(field_name, statuses, rows, data)
+ Redmine::Export::CSV.generate(:encoding => params[:encoding]) do |csv|
+ # csv headers
+ headers = [''] + statuses.map(&:name) + [l(:label_open_issues_plural), l(:label_closed_issues_plural), l(:label_total)]
+ csv << headers
+
+ # csv lines
+ rows.each do |row|
+ csv <<
+ [row.name] +
+ statuses.map{|s| aggregate(data, { field_name => row.id, 'status_id' => s.id })} +
+ [aggregate(data, { field_name => row.id, 'closed' => 0 })] +
+ [aggregate(data, { field_name => row.id, 'closed' => 1 })] +
+ [aggregate(data, { field_name => row.id })]
+ end
+ end
+ end
end
diff --git a/app/views/reports/_details.html.erb b/app/views/reports/_details.html.erb
index 3361117fc..3c6472743 100644
--- a/app/views/reports/_details.html.erb
+++ b/app/views/reports/_details.html.erb
@@ -25,6 +25,20 @@
<% end %>
</tbody>
</table>
+<% other_formats_links do |f| %>
+ <%= f.link_to_with_query_parameters 'CSV', {}, :onclick => "showModal('csv-export-options', '330px'); return false;" %>
+<% end %>
+<div id="csv-export-options" style="display: none;">
+ <h3 class="title"><%= l(:label_export_options, :export_format => 'CSV') %></h3>
+ <%= form_tag(project_issues_report_details_path(@project, :detail => params[:detail], :format => 'csv'), :method => :get, :id => 'csv-export-form') do %>
+ <%= export_csv_encoding_select_tag %>
+ <p class="buttons">
+ <%= submit_tag l(:button_export), :name => nil, :onclick => 'hideModal(this);', :data => {:disable_with => false} %>
+ <%= link_to_function l(:button_cancel), 'hideModal(this);' %>
+ </p>
+ <% end %>
+</div>
+
<div class="issue-report-graph hide-when-print">
<canvas id="issues_by_<%= params[:detail] %>"></canvas>
</div>
diff --git a/test/functional/reports_controller_test.rb b/test/functional/reports_controller_test.rb
index 37536f114..e2bbe7123 100644
--- a/test/functional/reports_controller_test.rb
+++ b/test/functional/reports_controller_test.rb
@@ -242,4 +242,91 @@ class ReportsControllerTest < Redmine::ControllerTest
)
assert_response 404
end
+
+ def test_issue_report_details_should_csv_export
+ %w(tracker version priority category assigned_to author subproject).each do |detail|
+ get(
+ :issue_report_details,
+ params: {
+ id: 1,
+ detail: detail,
+ format: 'csv'
+ }
+ )
+ assert_response :success
+ assert_equal 'text/csv; header=present', response.media_type
+ end
+ end
+
+ def test_issue_report_details_with_tracker_detail_should_csv_export
+ project = Project.find(1)
+ tracker = project.trackers.find_by(:name => 'Support request')
+ project.trackers.delete(tracker)
+
+ with_settings :display_subprojects_issues => '1' do
+ get(
+ :issue_report_details,
+ params: {
+ id: 1,
+ detail: 'tracker',
+ format: 'csv'
+ }
+ )
+ assert_response :success
+
+ assert_equal 'text/csv; header=present', response.media_type
+ lines = response.body.chomp.split("\n")
+ # Number of lines
+ rows = Project.find(1).rolled_up_trackers(true).visible
+ assert_equal rows.size + 1, lines.size
+ # Header
+ assert_equal '"",New,Assigned,Resolved,Feedback,Closed,Rejected,open,closed,Total', lines.first
+ # Details
+ to_test = [
+ 'Bug,5,0,0,0,3,0,5,3,8',
+ 'Feature request,0,1,0,0,0,0,1,0,1',
+ 'Support request,0,0,0,0,0,0,0,0,0'
+ ]
+ to_test.each do |expected|
+ assert_includes lines, expected
+ end
+ end
+ end
+
+ def test_issue_report_details_with_assigned_to_detail_should_csv_export
+ Issue.delete_all
+ Issue.generate!
+ Issue.generate!
+ Issue.generate!(:status_id => 5)
+ Issue.generate!(:assigned_to_id => 2)
+
+ with_settings :issue_group_assignment => '1' do
+ get(
+ :issue_report_details,
+ params: {
+ id: 1,
+ detail: 'assigned_to',
+ format: 'csv'
+ }
+ )
+ assert_response :success
+
+ assert_equal 'text/csv; header=present', response.media_type
+ lines = response.body.chomp.split("\n")
+ # Number of lines
+ rows = Project.find(1).principals.sorted + [I18n.t(:label_none)]
+ assert_equal rows.size + 1, lines.size
+ # Header
+ assert_equal '"",New,Assigned,Resolved,Feedback,Closed,Rejected,open,closed,Total', lines.first
+ # Details
+ to_test = [
+ 'Dave Lopper,0,0,0,0,0,0,0,0,0',
+ 'John Smith,1,0,0,0,0,0,1,0,1',
+ '[none] ,2,0,0,0,1,0,2,1,3'
+ ]
+ to_test.each do |expected|
+ assert_includes lines, expected
+ end
+ end
+ end
end