Project

General

Profile

« Previous | Next » 

Revision 63fbccbf

Added by Martin Bacovsky over 5 years ago

Fixes #25099 - Add macros for easier report definition

Add rows to a report with
<%- report_row(
'Column header 1': 'Value 1',
'Column header 2': `Value 2`,
) ->
and render the report with
<
= report_render -%>

Each row of the report needs to have the same columns.
report_render produces properly quoted valid CSV.
Values are converted to a String using to_s.
Enumerable values are comma separated and serialized.

Experimental render to YAML is included (report_render(format: :yaml)
Enumerable values are kept structured in this format.

View differences:

app/views/unattended/report_templates/host_statuses_csv.erb
snippet: false
model: ReportTemplate
-%>
Name,Global,<%= all_host_statuses.map { |s| s.status_name }.join(',') %>
<%- load_hosts(includes: :host_statuses).each_record do |host| -%>
<%= host.name -%>,<%= host.global_status -%>,<%= all_host_statuses.map { |s| host_status(host, s.status_name).status }.join(',') %>
<%- report_row({
'Name': host.name,
'Global': host.global_status
}.merge(all_host_statuses_hash(host))) -%>
<%- end -%>
<%= report_render -%>
lib/foreman/renderer/configuration.rb
:medium_uri,
:load_hosts,
:all_host_statuses,
:all_host_statuses_hash,
:host_status,
:preview?,
:raise
lib/foreman/renderer/scope/macros/base.rb
@all_host_statuses ||= HostStatus.status_registry.to_a.sort_by(&:status_name)
end
def all_host_statuses_hash(host)
all_host_statuses.map { |status| [status.status_name, host_status(host, status.status_name).status] }.to_h
end
def host_status(host, name)
klass = all_host_statuses.find { |status| status.status_name == name }
raise UnknownHostStatusError.new(status: name, statuses: all_host_statuses.map(&:status_name).join(',')) if klass.nil?
lib/foreman/renderer/scope/report.rb
module Renderer
module Scope
class Report < Foreman::Renderer::Scope::Base
def initialize(**args)
super
@report_data = []
@report_headers = []
end
def report_render(format: :csv)
case format
when :csv
report_render_csv
when :yaml
report_render_yaml
end
end
def report_row(row_data)
@report_headers = row_data.keys.map(&:to_s) if @report_headers.empty?
@report_data << row_data.values
end
def allowed_helpers
@allowed_helpers ||= super + [ :report_row, :report_render ]
end
private
def report_render_yaml
@report_data.map do |row|
valid_row = row.map { |cell| valid_yaml_type(cell) }
Hash[@report_headers.zip(valid_row)]
end.to_yaml
end
def report_render_csv
CSV.generate(headers: true, encoding: Encoding::UTF_8) do |csv|
csv << @report_headers
@report_data.each do |row|
csv << row.map { |cell| serialize_cell(cell) }
end
end
end
def serialize_cell(cell)
if cell.is_a?(Enumerable)
cell.map(&:to_s).join(',')
else
cell.to_s
end
end
def valid_yaml_type(cell)
if cell.is_a?(String) || [true, false].include?(cell) || cell.is_a?(Numeric) || cell.nil?
cell
elsif cell.is_a?(Enumerable)
cell.map { |item| valid_yaml_type(item) }
else
cell.to_s
end
end
end
end
end
test/unit/foreman/renderer/scope/report_test.rb
require 'test_helper'
class ReportScopeTest < ActiveSupport::TestCase
setup do
source = Foreman::Renderer::Source::String.new(content: '')
@scope = Foreman::Renderer::Scope::Report.new(source: source)
end
describe '#report_render' do
test 'render headers' do
@scope.report_row('Col1': 'Val1', 'Col2': 'Val2')
@scope.report_row('Col3': 'Val3', 'Col4': 'Val4')
expected_csv = "Col1,Col2\nVal1,Val2\nVal3,Val4\n"
assert_equal expected_csv, @scope.report_render(format: :csv)
expected_yaml = <<~OUT
---
- Col1: Val1
Col2: Val2
- Col1: Val3
Col2: Val4
OUT
assert_equal expected_yaml, @scope.report_render(format: :yaml)
end
test 'empty report' do
expected_csv = "\n"
assert_equal expected_csv, @scope.report_render(format: :csv)
expected_yaml = <<~OUT
--- []
OUT
assert_equal expected_yaml, @scope.report_render(format: :yaml)
end
test 'render types' do
@scope.report_row(
'List': ['Val1', 1, true],
'String': 'Text',
'Number': 1,
'Bool': false,
'Empty': '',
'Nil': nil
)
expected_csv = "List,String,Number,Bool,Empty,Nil\n\"Val1,1,true\",Text,1,false,\"\",\"\"\n"
assert_equal expected_csv, @scope.report_render(format: :csv)
expected_yaml = <<~OUT + " Nil: \n"
---
- List:
- Val1
- 1
- true
String: Text
Number: 1
Bool: false
Empty: ''
OUT
assert_equal expected_yaml, @scope.report_render(format: :yaml)
end
end
end

Also available in: Unified diff