Skip to content

Commit

Permalink
FEATURE: Export any type of report supporting table mode. (#7662)
Browse files Browse the repository at this point in the history
  • Loading branch information
nbianca authored and jjaffeux committed Jun 28, 2019
1 parent 847f294 commit b2eb0f4
Show file tree
Hide file tree
Showing 5 changed files with 75 additions and 24 deletions.
7 changes: 4 additions & 3 deletions app/assets/javascripts/admin/components/admin-report.js.es6
Expand Up @@ -55,7 +55,7 @@ export default Ember.Component.extend({
showTitle: true,
showFilteringUI: false,
showDatesOptions: Ember.computed.alias("model.dates_filtering"),
showExport: Ember.computed.not("model.onlyTable"),
showExport: Ember.computed.not("model.isTable"),
showRefresh: Ember.computed.or(
"showDatesOptions",
"model.available_filters.length"
Expand Down Expand Up @@ -170,8 +170,9 @@ export default Ember.Component.extend({
"[:prev_period]",
this.get("reportOptions.table.limit"),
customFilters
? JSON.stringify(customFilters, (key, value) =>
isNumeric(value) ? value.toString() : value
? JSON.stringify(
customFilters,
(key, value) => (isNumeric(value) ? value.toString() : value)
)
: null,
SCHEMA_VERSION
Expand Down
4 changes: 2 additions & 2 deletions app/assets/javascripts/admin/models/report.js.es6
Expand Up @@ -16,8 +16,8 @@ const Report = Discourse.Model.extend({
higher_is_better: true,

@computed("modes")
onlyTable(modes) {
return modes.length === 1 && modes[0] === "table";
isTable(modes) {
return modes.some(mode => mode === "table");
},

@computed("type", "start_date", "end_date")
Expand Down
23 changes: 16 additions & 7 deletions app/jobs/regular/export_csv_file.rb
Expand Up @@ -55,7 +55,7 @@ def execute(args)

# write to CSV file
CSV.open(absolute_path, "w") do |csv|
csv << get_header
csv << get_header if @entity != "report"
public_send(export_method).each { |d| csv << d }
end

Expand Down Expand Up @@ -186,14 +186,23 @@ def report_export
@extra[:category_id] = @extra[:category_id].present? ? @extra[:category_id].to_i : nil
@extra[:group_id] = @extra[:group_id].present? ? @extra[:group_id].to_i : nil

report_hash = {}
Report.find(@extra[:name], @extra).data.each do |row|
report_hash[row[:x].to_s] = row[:y].to_s
end
report = Report.find(@extra[:name], @extra)

header = []
titles = {}

(@extra[:start_date].to_date..@extra[:end_date].to_date).each do |date|
yield [date.to_s(:db), report_hash.fetch(date.to_s, 0)]
report.labels.each do |label|
if label[:type] == :user
titles[label[:properties][:username]] = label[:title]
header << label[:properties][:username]
else
titles[label[:property]] = label[:title]
header << label[:property]
end
end

yield header.map { |k| titles[k] || k }
report.data.each { |row| yield row.values_at(*header).map(&:to_s) }
end

def get_header
Expand Down
28 changes: 16 additions & 12 deletions app/models/report.rb
Expand Up @@ -17,6 +17,21 @@ def self.default_days
30
end

def self.default_labels
[
{
type: :date,
property: :x,
title: I18n.t("reports.default.labels.day")
},
{
type: :number,
property: :y,
title: I18n.t("reports.default.labels.count")
},
]
end

def initialize(type)
@type = type
@start_date ||= Report.default_days.days.ago.utc.beginning_of_day
Expand Down Expand Up @@ -102,18 +117,7 @@ def as_json(options = nil)
primary_color: self.primary_color,
secondary_color: self.secondary_color,
available_filters: self.available_filters.map { |k, v| { id: k }.merge(v) },
labels: labels || [
{
type: :date,
property: :x,
title: I18n.t("reports.default.labels.day")
},
{
type: :number,
property: :y,
title: I18n.t("reports.default.labels.count")
},
],
labels: labels || Report.default_labels,
average: self.average,
percent: self.percent,
higher_is_better: self.higher_is_better,
Expand Down
37 changes: 37 additions & 0 deletions spec/jobs/export_csv_file_spec.rb
Expand Up @@ -42,6 +42,43 @@
end
end

context '.report_export' do

let(:user) { Fabricate(:admin) }

let(:exporter) do
exporter = Jobs::ExportCsvFile.new
exporter.instance_variable_set(:@entity, 'report')
exporter.instance_variable_set(:@extra, HashWithIndifferentAccess.new(start_date: '2010-01-01', end_date: '2011-01-01'))
exporter.instance_variable_set(:@current_user, User.find_by(id: user.id))
exporter
end

it 'works with single-column reports' do
user.user_visits.create!(visited_at: '2010-01-01', posts_read: 42)
Fabricate(:user).user_visits.create!(visited_at: '2010-01-03', posts_read: 420)

exporter.instance_variable_get(:@extra)['name'] = 'dau_by_mau'
report = exporter.report_export.to_a

expect(report.first).to contain_exactly("Day", "Percent")
expect(report.second).to contain_exactly("2010-01-01", "100.0")
expect(report.third).to contain_exactly("2010-01-03", "50.0")
end

it 'works with multi-columns reports' do
DiscourseIpInfo.stubs(:get).with("1.1.1.1").returns(location: "Earth")
user.user_auth_token_logs.create!(action: "login", client_ip: "1.1.1.1", created_at: '2010-01-01')

exporter.instance_variable_get(:@extra)['name'] = 'staff_logins'
report = exporter.report_export.to_a

expect(report.first).to contain_exactly("User", "Location", "Login at")
expect(report.second).to contain_exactly(user.username, "Earth", "2010-01-01 00:00:00 UTC")
end

end

let(:user_list_header) {
%w{
id name username email title created_at last_seen_at last_posted_at
Expand Down

0 comments on commit b2eb0f4

Please sign in to comment.