Skip to content

Commit

Permalink
Add options for only showing grouped aggregate rows for reporting and
Browse files Browse the repository at this point in the history
rendering
  • Loading branch information
jonathangreenberg committed Mar 12, 2013
1 parent 80119f4 commit 66c96a3
Show file tree
Hide file tree
Showing 3 changed files with 127 additions and 121 deletions.
86 changes: 43 additions & 43 deletions lib/munger/data.rb
Original file line number Diff line number Diff line change
@@ -1,31 +1,31 @@
module Munger #:nodoc:

# this class is a data munger
# it takes raw data (arrays of hashes, basically)
# it takes raw data (arrays of hashes, basically)
# and can manipulate it in various interesting ways
class Data

attr_accessor :data

# will accept active record collection or array of hashes
def initialize(options = {})
@data = options[:data] if options[:data]
yield self if block_given?
end

def <<(data)
add_data(data)
end

def add_data(data)
if @data
@data = @data + data
@data = @data + data
else
@data = data
end
@data
end


#--
# NOTE:
Expand All @@ -37,14 +37,14 @@ def add_data(data)
def self.load_data(data, options = {})
Data.new(:data => data)
end

def columns
@columns ||= clean_data(@data.first).to_hash.keys
rescue
puts clean_data(@data.first).to_hash.inspect
end
# :default: The default value to use for the column in existing rows.

# :default: The default value to use for the column in existing rows.
# Set to nil if not specified.
# if a block is passed, you can set the values manually
def add_column(names, options = {})
Expand All @@ -55,7 +55,7 @@ def add_column(names, options = {})
else
col_data = default
end

if names.is_a? Array
names.each_with_index do |col, i|
row[col] = col_data[i]
Expand All @@ -69,7 +69,7 @@ def add_column(names, options = {})
alias :add_columns :add_column
alias :transform_column :add_column
alias :transform_columns :add_column

def clean_data(hash_or_ar)
if hash_or_ar.is_a? Hash
return Item.ensure(hash_or_ar)
Expand All @@ -78,48 +78,48 @@ def clean_data(hash_or_ar)
end
hash_or_ar
end

def filter_rows
new_data = []

@data.each do |row|
row = Item.ensure(row)
if (yield row)
new_data << row
end
end

@data = new_data
end

# group the data like sql
def group(groups, agg_hash = {})
data_hash = {}

agg_columns = []
agg_hash.each do |key, columns|
Data.array(columns).each do |col| # column name
agg_columns << col
end
end
agg_columns = agg_columns.uniq.compact

@data.each do |row|
row_key = Data.array(groups).map { |rk| row[rk] }
row_key = Data.array(groups).map { |rk| row[rk] || row.send(rk) }
data_hash[row_key] ||= {:cells => {}, :data => {}, :count => 0}
focus = data_hash[row_key]
focus[:data] = clean_data(row)

agg_columns.each do |col|
focus[:cells][col] ||= []
focus[:cells][col] << row[col]
focus[:cells][col] << (row[col] || row.send(col))
end
focus[:count] += 1
end

new_data = []
new_keys = []

data_hash.each do |row_key, data|
new_row = data[:data]
agg_hash.each do |key, columns|
Expand All @@ -128,15 +128,15 @@ def group(groups, agg_hash = {})
if key.is_a?(Array) && key[1].is_a?(Proc)
newcol = key[0].to_s + '_' + col.to_s
new_row[newcol] = key[1].call(data[:cells][col])
else
else
newcol = key.to_s + '_' + col.to_s
case key
when :average
sum = data[:cells][col].inject { |sum, a| sum + a }
new_row[newcol] = (sum / data[:count])
new_row[newcol] = (sum / data[:count])
when :count
new_row[newcol] = data[:count]
else
new_row[newcol] = data[:count]
else
new_row[newcol] = data[:cells][col].inject { |sum, a| sum + a }
end
end
Expand All @@ -145,14 +145,14 @@ def group(groups, agg_hash = {})
end
new_data << Item.ensure(new_row)
end

@data = new_data
new_keys.compact
end

def pivot(columns, rows, value, aggregation = :sum)
data_hash = {}

@data.each do |row|
column_key = Data.array(columns).map { |rk| row[rk] }
row_key = Data.array(rows).map { |rk| row[rk] }
Expand All @@ -163,46 +163,46 @@ def pivot(columns, rows, value, aggregation = :sum)
focus[:count] += 1
focus[:sum] += row[value]
end

new_data = []
new_keys = {}

data_hash.each do |row_key, row_hash|
new_row = {}
row_hash.each do |column_key, data|
column_key.each do |ckey|
new_row.merge!(data[:data])
case aggregation
when :average
new_row[ckey] = (data[:sum] / data[:count])
new_row[ckey] = (data[:sum] / data[:count])
when :count
new_row[ckey] = data[:count]
else
new_row[ckey] = data[:sum]
new_row[ckey] = data[:count]
else
new_row[ckey] = data[:sum]
end
new_keys[ckey] = true
end
end
new_data << Item.ensure(new_row)
end

@data = new_data
new_keys.keys
end

def self.array(string_or_array)
if string_or_array.is_a? Array
return string_or_array
else
return [string_or_array]
end
end

def size
@data.size
end
alias :length :size

def valid?
if ((@data.size > 0) &&
(@data.respond_to? :each_with_index) &&
Expand All @@ -225,8 +225,8 @@ def to_a(cols=nil)
end
array
end

end

end

41 changes: 22 additions & 19 deletions lib/munger/render/html.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,82 +8,85 @@
module Munger #:nodoc:
module Render #:nodoc:
class Html

attr_reader :report, :classes

def initialize(report, options = {})
@report = report
@skip_data = options[:skip_data]
set_classes(options[:classes])
end

def set_classes(options = nil)
options = {} if !options
default = {:table => 'report-table'}
@classes = default.merge(options)
end

def render
x = Builder::XmlMarkup.new

x.table(:class => @classes[:table]) do

x.tr do
@report.columns.each do |column|
x.th(:class => 'columnTitle') { x << @report.column_title(column) }
end
end

@report.process_data.each do |row|


process_data = @report.process_data
process_data = process_data.reject{|p| p[:meta][:data]} if @skip_data
process_data.each do |row|

classes = []
classes << row[:meta][:row_styles]
classes << 'group' + row[:meta][:group].to_s if row[:meta][:group]
classes << cycle('even', 'odd')
classes.compact!

if row[:meta][:group_header]
classes << 'groupHeader' + row[:meta][:group_header].to_s
classes << 'groupHeader' + row[:meta][:group_header].to_s
end

row_attrib = {}
row_attrib = {:class => classes.join(' ')} if classes.size > 0

x.tr(row_attrib) do
if row[:meta][:group_header]
header = row[:meta][:group_value].to_s
x.th(:colspan => @report.columns.size) { x << header }
else
else
@report.columns.each do |column|

cell_attrib = {}
if cst = row[:meta][:cell_styles]
cst = Item.ensure(cst)
if cell_styles = cst[column]
cell_attrib = {:class => cell_styles.join(' ')}
end
end

x.td(cell_attrib) { x << row[:data][column].to_s }
end
end
end
end

end
end

def cycle(one, two)
if @current == one
@current = two
else
@current = one
end
end

def valid?
@report.is_a? Munger::Report
end

end
end
end
Loading

0 comments on commit 66c96a3

Please sign in to comment.