diff options
author | Go MAEDA <maeda@farend.jp> | 2022-07-21 04:13:37 +0000 |
---|---|---|
committer | Go MAEDA <maeda@farend.jp> | 2022-07-21 04:13:37 +0000 |
commit | 0c037a7b48e061fa32e96c21718b30560431c4c7 (patch) | |
tree | feba0bcd09960e044151cdafd0b69f740b098918 | |
parent | cfbc2e7a33cb068f1dd71b06a2625ebeecd56ccb (diff) | |
download | redmine-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.rb | 9 | ||||
-rw-r--r-- | app/helpers/reports_helper.rb | 18 | ||||
-rw-r--r-- | app/views/reports/_details.html.erb | 14 | ||||
-rw-r--r-- | test/functional/reports_controller_test.rb | 87 |
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 |