Files
openfoodnetwork/lib/reporting/report_renderer.rb
2025-10-01 13:59:43 +10:00

119 lines
2.8 KiB
Ruby

# frozen_string_literal: true
require 'csv'
require 'spreadsheet_architect'
module Reporting
class ReportRenderer
REPORT_FORMATS = [:csv, :json, :html, :xlsx, :pdf].freeze
def initialize(report)
@report = report
end
# Strip header and summary rows for these formats
def raw_render?
@report.params[:report_format].in?(['csv'])
end
# Do not format values for these output formats
def unformatted_render?
@report.params[:report_format].in?(['json', 'csv'])
end
def html_render?
@report.params[:report_format].in?([nil, '', 'pdf'])
end
def display_metadata_rows?
@report.params[:display_metadata_rows].present? && raw_render?
end
def display_header_row?
@report.params[:display_header_row].present? && !raw_render?
end
def display_summary_row?
@report.params[:display_summary_row].present? && !raw_render?
end
def table_headers
base = @report.table_headers || []
return base unless display_metadata_rows?
[*metadata_headers, base]
end
def table_rows
@report.table_rows || []
end
def metadata_headers
return [] unless display_metadata_rows?
Reporting::ReportMetadataBuilder.new(@report, @report.try(:user)).rows
end
def as_json(_context_controller = nil)
@report.rows.map(&:to_h).as_json
end
def render_as(target_format)
unless target_format.to_sym.in?(REPORT_FORMATS)
raise ActionController::BadRequest, "report_format should be in #{REPORT_FORMATS}"
end
public_send("to_#{target_format}")
end
def to_html(layout: nil)
ApplicationController.render(
template: "admin/reports/_table",
layout:,
locals: { report: @report }
)
end
def to_csv
SpreadsheetArchitect.to_csv(headers: table_headers, data: table_rows)
end
def to_xlsx
SpreadsheetArchitect.to_xlsx(spreadsheets_options)
end
def to_pdf
html = to_html(layout: "pdf")
WickedPdf.new.pdf_from_string(html)
end
private
def spreadsheets_options
{
headers: table_headers,
data: table_rows,
freeze_headers: true,
row_style: spreadsheets_style,
header_style: spreadsheets_style.merge({ bg_color: "f7f6f6", bold: true }),
conditional_row_styles: [
{
# Detect header_row: the row is nil except for first cell
if: proc { |row_data, _row_index|
row_data.compact.count == 1 && row_data[0].present?
},
styles: { font_size: 12, bold: true }
},
],
}
end
def spreadsheets_style
{
font_name: 'system-ui',
alignment: { horizontal: :left, vertical: :bottom }
}
end
end
end