このコミットが含まれているのは:
@@ -10,6 +10,7 @@ class CountsTableExport
|
||||
raise ArgumentError if (params.keys - CountsTableQuery::KEYS - EXTRA).any?
|
||||
raise ArgumentError unless params.values.all? { _1.is_a?(String) }
|
||||
|
||||
params = CountsDefaults.for(params).merge(params)
|
||||
unless params.key?('children')
|
||||
params = params.merge('children' => CountsExportProjection.default_children(
|
||||
params.fetch('resource', 'posts')).join(','))
|
||||
@@ -63,7 +64,8 @@ class CountsTableExport
|
||||
total = @summary ? nil : page[:count]
|
||||
progress&.call(processed, total)
|
||||
unless started
|
||||
metadata = page.except(:versions, :cells, :axis_labels, :column_values).merge(
|
||||
metadata = page.except(:versions, :cells, :axis_labels, :column_values,
|
||||
:row_headers, :column_headers, :pivot_totals).merge(
|
||||
resource:, filters: @params.slice(*CountsTableQuery::KEYS),
|
||||
fields: @query.population.definition.metadata,
|
||||
columns: @columns, column_labels: @headers,
|
||||
@@ -71,12 +73,35 @@ class CountsTableExport
|
||||
sort: @params.fetch('sort', ''), child_sort: @params['child_sort'])
|
||||
output << (format == 'json' ? "{\"metadata\":#{ metadata.to_json },\"data\":[" : "\uFEFF")
|
||||
if format == 'csv'
|
||||
headers = @summary ? summary_headers : @headers
|
||||
output << CSV.generate_line(headers.map { safe(_1) }, row_sep: "\r\n")
|
||||
headings = @summary ? pivot_headers(page) : [@headers]
|
||||
headings.each do |headers|
|
||||
output << CSV.generate_line(headers.map { safe(_1) }, row_sep: "\r\n")
|
||||
end
|
||||
end
|
||||
started = true
|
||||
end
|
||||
(page[:cells] || page[:versions]).each do |row|
|
||||
if @summary
|
||||
pivot_rows(page).each do |header|
|
||||
records = page.fetch(:column_headers).map { pivot_cell(header, _1, page) }
|
||||
if format == 'csv'
|
||||
labels = pivot_row_labels(header, page)
|
||||
values = labels + records.map { _1[:count] }
|
||||
output << CSV.generate_line(values.map { safe(_1) }, row_sep: "\r\n")
|
||||
processed += records.length
|
||||
progress&.call(processed, nil)
|
||||
else
|
||||
records.each do |record|
|
||||
output << ',' unless first
|
||||
output << record.to_json
|
||||
first = false
|
||||
processed += 1
|
||||
progress&.call(processed, nil)
|
||||
end
|
||||
end
|
||||
end
|
||||
next
|
||||
end
|
||||
page[:versions].each do |row|
|
||||
record = project(row)
|
||||
if format == 'json'
|
||||
output << ',' unless first
|
||||
@@ -106,12 +131,76 @@ class CountsTableExport
|
||||
|
||||
private
|
||||
|
||||
def summary_headers
|
||||
return @headers if @headers.any?
|
||||
def pivot_rows(page)
|
||||
page.fetch(:row_headers).reject do |header|
|
||||
header[:kind] == 'grand_total' && @query.page * @query.limit < page[:group_count]
|
||||
end
|
||||
end
|
||||
|
||||
labels = @labels
|
||||
@query.axes.all.map { labels.fetch(_1['field'], _1['field']) } +
|
||||
[labels.fetch('count', 'count')]
|
||||
def total_label(header)
|
||||
@labels.fetch(header[:kind] == 'grand_total' ? 'grandTotal' : 'subtotal', '')
|
||||
end
|
||||
|
||||
def pivot_row_labels(header, page)
|
||||
values = header[:values].each_with_index.map { |value, index|
|
||||
summary_value(value, index, page)
|
||||
}
|
||||
values << total_label(header) unless header[:kind] == 'data'
|
||||
leading = [@query.axes.rows.length, @query.axes.columns.any? ? 1 : 0].max
|
||||
Array.new(leading) { values[_1] || '' }
|
||||
end
|
||||
|
||||
def pivot_headers(page)
|
||||
row_count = @query.axes.rows.length
|
||||
cols = @query.axes.columns
|
||||
leading = [row_count, cols.any? ? 1 : 0].max
|
||||
columns = page.fetch(:column_headers)
|
||||
Array.new([cols.length, 1].max) do |level|
|
||||
prefix = Array.new(leading) { level.zero? ? @headers[_1].to_s : '' }
|
||||
prefix = [''] if row_count.zero? && cols.any?
|
||||
prefix + columns.each_with_index.map do |header, index|
|
||||
values = header[:values]
|
||||
if cols.empty?
|
||||
header[:kind] == 'data' ? @labels.fetch('count', '') : total_label(header)
|
||||
elsif level == values.length && header[:kind] != 'data'
|
||||
total_label(header)
|
||||
elsif level < values.length
|
||||
previous = index.positive? ? columns[index - 1][:values] : nil
|
||||
if level < cols.length - 1 && previous && previous.first(level + 1) == values.first(level + 1)
|
||||
''
|
||||
else
|
||||
summary_value(values[level], row_count + level, page)
|
||||
end
|
||||
else
|
||||
''
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def pivot_cell(row, column, page)
|
||||
@pivot_lookup ||= {}
|
||||
# Discard the previous page's lookup; memory remains bounded by one page and its rollups.
|
||||
unless @pivot_page.equal?(page)
|
||||
@pivot_page = page
|
||||
@pivot_lookup = page.fetch(:cells).to_h do |cell|
|
||||
values = cell[:values]
|
||||
[[values.first(@query.axes.rows.length), values.drop(@query.axes.rows.length)], cell[:count]]
|
||||
end
|
||||
page.fetch(:pivot_totals).each { @pivot_lookup[[_1[:row], _1[:column]]] = _1[:count] }
|
||||
end
|
||||
kinds = [row[:kind], column[:kind]]
|
||||
kind = if kinds.include?('grand_total')
|
||||
'grand_total'
|
||||
elsif kinds.include?('subtotal')
|
||||
'subtotal'
|
||||
else
|
||||
'data'
|
||||
end
|
||||
{ row: row[:values], column: column[:values], kind:,
|
||||
row_kind: row[:kind], column_kind: column[:kind],
|
||||
row_depth: row[:values].length, column_depth: column[:values].length,
|
||||
count: @pivot_lookup.fetch([row[:values], column[:values]], 0) }
|
||||
end
|
||||
|
||||
def summary_value(value, index, page)
|
||||
|
||||
新しいイシューから参照
ユーザーをブロックする