@@ -112,7 +112,10 class TimelogController < ApplicationController | |||||
112 | end |
|
112 | end | |
113 | end |
|
113 | end | |
114 |
|
114 | |||
115 | render :layout => false if request.xhr? |
|
115 | respond_to do |format| | |
|
116 | format.html { render :layout => !request.xhr? } | |||
|
117 | format.csv { send_data(report_to_csv(@criterias, @periods, @hours).read, :type => 'text/csv; header=present', :filename => 'timelog.csv') } | |||
|
118 | end | |||
116 | end |
|
119 | end | |
117 |
|
120 | |||
118 | def details |
|
121 | def details |
@@ -76,4 +76,56 module TimelogHelper | |||||
76 | export.rewind |
|
76 | export.rewind | |
77 | export |
|
77 | export | |
78 | end |
|
78 | end | |
|
79 | ||||
|
80 | def report_to_csv(criterias, periods, hours) | |||
|
81 | export = StringIO.new | |||
|
82 | CSV::Writer.generate(export, l(:general_csv_separator)) do |csv| | |||
|
83 | # Column headers | |||
|
84 | headers = criterias.collect {|criteria| l(@available_criterias[criteria][:label]) } | |||
|
85 | headers += periods | |||
|
86 | headers << l(:label_total) | |||
|
87 | csv << headers.collect {|c| to_utf8(c) } | |||
|
88 | # Content | |||
|
89 | report_criteria_to_csv(csv, criterias, periods, hours) | |||
|
90 | # Total row | |||
|
91 | row = [ l(:label_total) ] + [''] * (criterias.size - 1) | |||
|
92 | total = 0 | |||
|
93 | periods.each do |period| | |||
|
94 | sum = sum_hours(select_hours(hours, @columns, period.to_s)) | |||
|
95 | total += sum | |||
|
96 | row << (sum > 0 ? "%.2f" % sum : '') | |||
|
97 | end | |||
|
98 | row << "%.2f" %total | |||
|
99 | csv << row | |||
|
100 | end | |||
|
101 | export.rewind | |||
|
102 | export | |||
|
103 | end | |||
|
104 | ||||
|
105 | def report_criteria_to_csv(csv, criterias, periods, hours, level=0) | |||
|
106 | hours.collect {|h| h[criterias[level]]}.uniq.each do |value| | |||
|
107 | hours_for_value = select_hours(hours, criterias[level], value) | |||
|
108 | next if hours_for_value.empty? | |||
|
109 | row = [''] * level | |||
|
110 | row << to_utf8(value.nil? ? l(:label_none) : @available_criterias[criterias[level]][:klass].find_by_id(value)) | |||
|
111 | row += [''] * (criterias.length - level - 1) | |||
|
112 | total = 0 | |||
|
113 | periods.each do |period| | |||
|
114 | sum = sum_hours(select_hours(hours_for_value, @columns, period.to_s)) | |||
|
115 | total += sum | |||
|
116 | row << (sum > 0 ? "%.2f" % sum : '') | |||
|
117 | end | |||
|
118 | row << "%.2f" %total | |||
|
119 | csv << row | |||
|
120 | ||||
|
121 | if criterias.length > level + 1 | |||
|
122 | report_criteria_to_csv(csv, criterias, periods, hours_for_value, level + 1) | |||
|
123 | end | |||
|
124 | end | |||
|
125 | end | |||
|
126 | ||||
|
127 | def to_utf8(s) | |||
|
128 | @ic ||= Iconv.new(l(:general_csv_encoding), 'UTF-8') | |||
|
129 | begin; @ic.iconv(s.to_s); rescue; s.to_s; end | |||
|
130 | end | |||
79 | end |
|
131 | end |
@@ -60,6 +60,11 | |||||
60 | </tr> |
|
60 | </tr> | |
61 | </tbody> |
|
61 | </tbody> | |
62 | </table> |
|
62 | </table> | |
|
63 | ||||
|
64 | <p class="other-formats"> | |||
|
65 | <%= l(:label_export_to) %> | |||
|
66 | <span><%= link_to 'CSV', params.merge({:format => 'csv'}), :class => 'csv' %></span> | |||
|
67 | </p> | |||
63 | <% end %> |
|
68 | <% end %> | |
64 | <% end %> |
|
69 | <% end %> | |
65 |
|
70 |
@@ -118,7 +118,18 class TimelogControllerTest < Test::Unit::TestCase | |||||
118 | assert_template 'report' |
|
118 | assert_template 'report' | |
119 | assert_not_nil assigns(:total_hours) |
|
119 | assert_not_nil assigns(:total_hours) | |
120 | assert_equal "0.00", "%.2f" % assigns(:total_hours) |
|
120 | assert_equal "0.00", "%.2f" % assigns(:total_hours) | |
121 | end |
|
121 | end | |
|
122 | ||||
|
123 | def test_report_csv_export | |||
|
124 | get :report, :project_id => 1, :columns => 'month', :from => "2007-01-01", :to => "2007-06-30", :criterias => ["project", "member", "activity"], :format => "csv" | |||
|
125 | assert_response :success | |||
|
126 | assert_equal 'text/csv', @response.content_type | |||
|
127 | lines = @response.body.chomp.split("\n") | |||
|
128 | # Headers | |||
|
129 | assert_equal 'Project,Member,Activity,2007-1,2007-2,2007-3,2007-4,2007-5,2007-6,Total', lines.first | |||
|
130 | # Total row | |||
|
131 | assert_equal 'Total,"","","","",154.25,8.65,"","",162.90', lines.last | |||
|
132 | end | |||
122 |
|
133 | |||
123 | def test_details_at_project_level |
|
134 | def test_details_at_project_level | |
124 | get :details, :project_id => 1 |
|
135 | get :details, :project_id => 1 |
General Comments 0
You need to be logged in to leave comments.
Login now