Files
openfoodnetwork/lib/reporting/report_renderer.rb
Maikel Linke a177f4c066 Add feature to render reports in the background
This is supposed to lower the memory footprint of all Puma workers. The
reports code will occupy needed memory in one Sidekiq worker instead of
in several Puma processes.

The current code doesn't limit the execution time yet. We either need a
way to terminate the report rendering after a while or send an email
with a link to access a rendered report.
2023-01-18 23:12:26 +00:00

99 lines
2.2 KiB
Ruby

# frozen_string_literal: true
require 'spreadsheet_architect'
module Reporting
class ReportRenderer
REPORT_FORMATS = [:csv, :json, :html, :xlsx, :pdf].freeze
def initialize(report)
@report = report
end
def raw_render?
@report.params[:report_format].in?(['json', 'csv'])
end
def html_render?
@report.params[:report_format].in?([nil, '', 'pdf'])
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
@report.table_headers || []
end
def table_rows
@report.table_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: 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