mirror of
https://github.com/redmine/redmine.git
synced 2025-11-14 09:16:02 +01:00
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
This commit is contained in:
@@ -21,6 +21,7 @@ class ReportsController < ApplicationController
|
|||||||
menu_item :issues
|
menu_item :issues
|
||||||
before_action :find_project, :authorize, :find_issue_statuses
|
before_action :find_project, :authorize, :find_issue_statuses
|
||||||
|
|
||||||
|
include ReportsHelper
|
||||||
def issue_report
|
def issue_report
|
||||||
with_subprojects = Setting.display_subprojects_issues?
|
with_subprojects = Setting.display_subprojects_issues?
|
||||||
@trackers = @project.rolled_up_trackers(with_subprojects).visible
|
@trackers = @project.rolled_up_trackers(with_subprojects).visible
|
||||||
@@ -82,6 +83,14 @@ class ReportsController < ApplicationController
|
|||||||
else
|
else
|
||||||
render_404
|
render_404
|
||||||
end
|
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
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|||||||
@@ -44,4 +44,22 @@ module ReportsHelper
|
|||||||
parameters = {:set_filter => 1, :subproject_id => '!*', field => (row.id || '!*')}.merge(options)
|
parameters = {:set_filter => 1, :subproject_id => '!*', field => (row.id || '!*')}.merge(options)
|
||||||
project_issues_path(row.is_a?(Project) ? row : project, parameters)
|
project_issues_path(row.is_a?(Project) ? row : project, parameters)
|
||||||
end
|
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
|
end
|
||||||
|
|||||||
@@ -25,6 +25,20 @@
|
|||||||
<% end %>
|
<% end %>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</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">
|
<div class="issue-report-graph hide-when-print">
|
||||||
<canvas id="issues_by_<%= params[:detail] %>"></canvas>
|
<canvas id="issues_by_<%= params[:detail] %>"></canvas>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -242,4 +242,91 @@ class ReportsControllerTest < Redmine::ControllerTest
|
|||||||
)
|
)
|
||||||
assert_response 404
|
assert_response 404
|
||||||
end
|
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
|
end
|
||||||
|
|||||||
Reference in New Issue
Block a user