Created
May 30, 2012 17:10
-
-
Save faraazkhan/2837702 to your computer and use it in GitHub Desktop.
Reporting Engine Sample Code
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class Reporter | |
include ActionController::UrlWriter | |
include ActionController::Routing::Helpers | |
attr_accessor :models_to_include | |
def initialize(params) | |
@params = replace_placeholder_values(params.dup) | |
if @params['klass'] | |
@query_object = self | |
self.models_to_include = [] | |
@order, @order_metadata = build_order_conditions | |
@limit = @params.delete(:limit) || 1000 | |
@params.delete(:include) # don't allow this | |
@filters = @params.delete(:filters) | |
@filters = @filters.first if @filters.present? | |
@klass = @params.delete('klass').constantize | |
elsif @params['custom_query'] | |
@query_object = Kernel.const_get("CustomQueries").const_get(@params['custom_query']).new(@params, self) | |
@filters = @params.delete(:filters) | |
@filters = @filters.first if @filters.present? | |
end | |
end | |
def build_order_conditions | |
cols = {} | |
order = [] | |
order_metadata = [] | |
traits = user_selected_attributes | |
@params.each do |key, value| | |
if matches = key.to_s.match(/^(asc|desc)end_by_(.*)$/) | |
order[value.to_i] = [ traits.index(matches[2]), (matches[1] === 'asc' ? 1 : -1) ] | |
order_metadata[value.to_i] = [ matches[2], matches[1] ] | |
end | |
end | |
@params.delete_if { |key, value| key =~ /^(asc|desc)end_by_(.*)$/ } | |
return order, order_metadata | |
end | |
def generate(options = {}) | |
format = options[:format] || :json | |
case format | |
when :csv | |
FasterCSV.generate do |csv| | |
csv << @query_object.user_selected_attributes.map { |elem| label(elem) } | |
@query_object.to_sorted_grid(:no_link => true).each do |row| | |
csv << row | |
end | |
end | |
else | |
{ | |
:metadata => { :order => (@order_metadata || []), :columns => @query_object.user_selected_attributes.map{|att| label(att)} }, | |
:data => @query_object.to_sorted_grid | |
} | |
end | |
end | |
def matching_records | |
klass.searchlogic(@params).all(:limit => @limit) | |
end | |
def user_selected_attributes | |
return @user_selected_attrs if @user_selected_attrs | |
@user_selected_attrs = @params.delete(:select) || [] | |
@user_selected_attrs.delete_if { |a| a.blank? } | |
@user_selected_attrs | |
end | |
def klass | |
@klass ||= @params.delete('klass').try(:constantize) | |
end | |
def to_sorted_grid(options = {}) | |
result = format( sort( filter( to_record_grid(options) ) ) ) | |
if options[:no_link] | |
result.each { |r| r.slice!(0) } | |
end | |
result | |
end | |
# Redirect the user to the correct drill-down detail page for each | |
# type of record, instead of relying on the item at the tail end of | |
# the lineage. | |
def drilldown_url_for(record) | |
case record | |
when Complaint, RequiredAction, Mandate, MandateRespondent | |
polymorphic_path(record.lineage) | |
when Task, Meeting | |
polymorphic_path(record.workitem.lineage) | |
when EventRecord | |
url_for(record.auditable) | |
end | |
end | |
def to_record_grid(options = {}) | |
records = matching_records | |
traits = user_selected_attributes | |
results = [] | |
records.each do |record| | |
row = [drilldown_url_for(record)] | |
traits.each do |col| | |
obj = record | |
col.split('.').each do |msg| | |
break if msg.nil? || obj.nil? || (msg != 'count' && (obj.respond_to?(:traits) && !obj.class.traits.include?(msg))) | |
val = obj.__send__(msg) | |
val = val.name if msg == 'type' | |
obj = val | |
end | |
row << obj | |
end | |
@types ||= Reporter.extract_types(row) | |
results << row | |
end | |
results | |
end | |
def self.extract_types(row) | |
types = [] | |
row.each_with_index do |item, i| | |
if item.is_a?(DateTime) || item.is_a?(ActiveSupport::TimeWithZone) | |
types[i] = 'DateTime' | |
elsif item.is_a?(Date) | |
types[i] = 'Date' | |
elsif item.is_a?(TrueClass) || item.is_a?(FalseClass) | |
types[i] = 'Boolean' | |
else | |
types[i] = item.class.to_s | |
end | |
end | |
types | |
end | |
def format(grid) | |
student_id_index = @user_selected_attrs.index('student_id') | |
grid.each do |row| | |
student_id_index ||= row.length | |
row.each_with_index do |item, i| | |
item ||= false if (student_id_index + 1) == i | |
next if item.nil? | |
if @types[i] == 'Boolean' | |
row[i] = item ? 'Yes' : 'No' | |
elsif @types[i] == 'Date' | |
row[i] = Date.parse(item.to_s).strftime("%m/%d/%Y") | |
elsif @types[i] == 'DateTime' | |
row[i] = DateTime.parse(item.to_s).strftime("%m/%d/%Y %l:%M%p") | |
elsif (student_id_index + 1) == i && row[i].blank? | |
row[i] = 'Student Not Identified' | |
end | |
end | |
end | |
grid | |
end | |
def extract_filter_metadata | |
filters = [] | |
return filters unless @filters | |
traits = @query_object.user_selected_attributes | |
@filters.each do |key, value| | |
if matches = key.to_s.match(/^(.*)_(does_not_equal|equals|gte|lte)$/) | |
operator = '' | |
select_or_reject = '' | |
datatype_method = case matches[1] | |
when /_date_?/, /_at/ | |
'to_date' | |
else | |
'to_s' | |
end | |
case matches[2] | |
when 'equals' | |
operator = '==' | |
select_or_reject = 'select' | |
when 'does_not_equal' | |
operator = '==' | |
select_or_reject = 'reject' | |
when 'gte' | |
operator = '>=' | |
select_or_reject = 'select' | |
when 'lte' | |
operator = '<=' | |
select_or_reject = 'select' | |
end | |
if traits.index(matches[1]) | |
filters << [ select_or_reject, datatype_method, traits.index(matches[1]), operator, value ] | |
end | |
end | |
end | |
filters | |
end | |
def filter(grid) | |
filters = extract_filter_metadata | |
traits = @query_object.user_selected_attributes | |
#debugger if traits.include?("Timeliness Status") | |
filters.each do |filter| | |
if filter[0] == 'select' | |
grid = grid.select do |item| | |
item[filter[2] + 1].try(filter[1]).try(filter[3], filter[4].try(filter[1])) | |
end.compact | |
elsif filter[0] == 'reject' | |
grid = grid.reject do |item| | |
item[filter[2] + 1].try(filter[1]).try(filter[3], filter[4].try(filter[1])) | |
end.compact | |
end | |
end | |
# optional - be sensitive to datatype (int, date, etc) | |
grid | |
end | |
def sort(grid) | |
grid.sort! do |elem1, elem2| | |
ret_val = 0 | |
@order.each do |colidx, order_type| | |
i = colidx + 1 # we have to ignore the id column | |
if elem1[i] != elem2[i] | |
ret_val = (order_type * (elem1[i] <=> elem2[i])) | |
break | |
end | |
end | |
ret_val | |
end | |
end | |
def label(attr) | |
return attr unless klass | |
attr = attr.sub(/\.to_(.*)$/,'') | |
attr_list = attr.split('.') | |
default_value = klass.human_attribute_name(attr.gsub('.','_')).titleize | |
keys = [] | |
keys << attr_list[-2..-1].join('.') if attr_list[-2..-1] | |
keys << attr_list[-2] if attr_list[-2] | |
keys << attr_list.last | |
keys.map! {|key| "reporter.#{key}".to_sym } | |
keys << default_value | |
new_label = I18n.t keys.first, :default => keys | |
new_label | |
end | |
private | |
def replace_placeholder_values(params) | |
params.each do |key, value| | |
if value.is_a?(String) && value =~ /\<(.*)\>/ | |
params[key] = replacement_value($~[1]) | |
end | |
end | |
if params[:filters].present? | |
params[:filters].first.each do |key, value| | |
if value.is_a?(String) && value =~ /\<(.*)\>/ | |
params[:filters].first[key] = replacement_value($~[1]) | |
end | |
end | |
end | |
if params[:pre_filter].present? | |
params[:pre_filter].each do |key, value| | |
if value.is_a?(String) && value =~ /\<(.*)\>/ | |
params[:pre_filter][key] = replacement_value($~[1]) | |
end | |
end | |
end | |
return params | |
end | |
def replacement_value(placeholder) | |
case placeholder | |
when 'current_user_id' | |
User.current.id | |
when 'now' | |
Time.now | |
when /(\d+)_days_from_now/ | |
$~[1].to_i.days.from_now.to_date.to_s | |
when /(\d+)_days_ago/ | |
$~[1].to_i.days.ago.to_date.to_s | |
when /^(beginning|end)_of_month$/ | |
DateTime.now.send(placeholder) | |
when 'court_year_start' | |
COURT_YEAR_START | |
when 'court_year_end' | |
COURT_YEAR_END | |
end | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment