lib/saulabs/reportable/report_cache.rb in reportable-1.2.0 vs lib/saulabs/reportable/report_cache.rb in reportable-1.3.0
- old
+ new
@@ -18,11 +18,11 @@
validates_presence_of :grouping
validates_presence_of :aggregation
validates_presence_of :value
validates_presence_of :reporting_period
- attr_accessible :model_name, :report_name, :grouping, :aggregation, :value, :reporting_period, :conditions
+ # attr_accessible :model_name, :report_name, :grouping, :aggregation, :value, :reporting_period, :conditions
self.skip_time_zone_conversion_for_attributes = [:reporting_period]
# Clears the cache for the specified +klass+ and +report+
#
@@ -38,14 +38,11 @@
# end
#
# Saulabs::Reportable::ReportCache.clear_for(User, :registrations)
#
def self.clear_for(klass, report)
- self.delete_all(:conditions => {
- :model_name => klass.name,
- :report_name => report.to_s
- })
+ self.where(model_name: klass.name, report_name: report.to_s).delete_all
end
# Processes the report using the respective cache.
#
# @param [Saulabe::Reportable::Report] report
@@ -67,22 +64,31 @@
# @return [ResultSet<Array<DateTime, Float>>]
# the result of the report as pairs of {DateTime}s and {Float}s
#
def self.process(report, options, &block)
raise ArgumentError.new('A block must be given') unless block_given?
+
+ # If end_date is in the middle of the current reporting period it means it requests live_data.
+ # Update the options hash to reflect reality.
+ current_reporting_period = ReportingPeriod.new(options[:grouping])
+ if options[:end_date] && options[:end_date] > current_reporting_period.date_time
+ options[:live_data] = true
+ options.delete(:end_date)
+ end
+
self.transaction do
cached_data = read_cached_data(report, options)
new_data = read_new_data(cached_data, options, &block)
prepare_result(new_data, cached_data, report, options)
end
end
private
def self.prepare_result(new_data, cached_data, report, options)
- new_data = new_data.map { |data| [ReportingPeriod.from_db_string(options[:grouping], data[0]), data[1]] }
- cached_data.map! { |cached| [ReportingPeriod.new(options[:grouping], cached.reporting_period), cached.value] }
+ new_data = new_data.to_a.map { |data| [ReportingPeriod.from_db_string(options[:grouping], data[0]), data[1]] }
+ cached_data.to_a.map! { |cached| [ReportingPeriod.new(options[:grouping], cached.reporting_period), cached.value] }
current_reporting_period = ReportingPeriod.new(options[:grouping])
reporting_period = get_first_reporting_period(options)
result = []
while reporting_period < (options[:end_date] ? ReportingPeriod.new(options[:grouping], options[:end_date]).next : current_reporting_period)
if cached = cached_data.find { |cached| reporting_period == cached[0] }
@@ -126,61 +132,66 @@
conditions.empty? ? '' : conditions.to_s
end
end
def self.read_cached_data(report, options)
- options[:conditions] ||= []
- conditions = [
- %w(model_name report_name grouping aggregation conditions).map do |column_name|
- "#{self.connection.quote_column_name(column_name)} = ?"
- end.join(' AND '),
- report.klass.to_s,
- report.name.to_s,
- options[:grouping].identifier.to_s,
- report.aggregation.to_s,
- serialize_conditions(options[:conditions])
- ]
- first_reporting_period = get_first_reporting_period(options)
- last_reporting_period = get_last_reporting_period(options)
- if last_reporting_period
- conditions.first << ' AND reporting_period BETWEEN ? AND ?'
- conditions << first_reporting_period.date_time
- conditions << last_reporting_period.date_time
+ conditions = build_conditions_for_reading_cached_data(report, options)
+ conditions.limit(options[:limit]).order('reporting_period ASC')
+ end
+
+ def self.build_conditions_for_reading_cached_data(report, options)
+ start_date = get_first_reporting_period(options).date_time
+
+ conditions = where('reporting_period >= ?', start_date).where(
+ model_name: report.klass.to_s,
+ report_name: report.name.to_s,
+ grouping: options[:grouping].identifier.to_s,
+ aggregation: report.aggregation.to_s,
+ conditions: serialize_conditions(options[:conditions] || [])
+ )
+
+ if options[:end_date]
+ end_date = ReportingPeriod.new(options[:grouping], options[:end_date]).date_time
+ conditions.where('reporting_period <= ?', end_date)
else
- conditions.first << ' AND reporting_period >= ?'
- conditions << first_reporting_period.date_time
+ conditions
end
- self.all(
- :conditions => conditions,
- :limit => options[:limit],
- :order => 'reporting_period ASC'
- )
end
def self.read_new_data(cached_data, options, &block)
- if !options[:live_data] && cached_data.length == options[:limit]
- []
+ return [] if !options[:live_data] && cached_data.size == options[:limit]
+
+ first_reporting_period_to_read = get_first_reporting_period_to_read(cached_data, options)
+ last_reporting_period_to_read = options[:end_date] ? ReportingPeriod.new(options[:grouping], options[:end_date]).last_date_time : nil
+
+ yield(first_reporting_period_to_read.date_time, last_reporting_period_to_read)
+ end
+
+ def self.get_first_reporting_period_to_read(cached_data, options)
+ return get_first_reporting_period(options) if cached_data.empty?
+
+ last_cached_reporting_period = ReportingPeriod.new(options[:grouping], cached_data.last.reporting_period)
+ missing_reporting_periods = options[:limit] - cached_data.length
+ last_reporting_period = if !options[:live_data] && options[:end_date]
+ ReportingPeriod.new(options[:grouping], options[:end_date])
else
- first_reporting_period_to_read = if cached_data.length < options[:limit]
- get_first_reporting_period(options)
- else
- ReportingPeriod.new(options[:grouping], cached_data.last.reporting_period).next
- end
- last_reporting_period_to_read = options[:end_date] ? ReportingPeriod.new(options[:grouping], options[:end_date]).last_date_time : nil
- yield(first_reporting_period_to_read.date_time, last_reporting_period_to_read)
+ ReportingPeriod.new(options[:grouping]).previous
end
+
+ if missing_reporting_periods == 0 || last_cached_reporting_period.offset(missing_reporting_periods) == last_reporting_period
+ # cache only has missing data contiguously at the end
+ last_cached_reporting_period.next
+ else
+ get_first_reporting_period(options)
+ end
end
def self.get_first_reporting_period(options)
if options[:end_date]
ReportingPeriod.first(options[:grouping], options[:limit] - 1, options[:end_date])
else
ReportingPeriod.first(options[:grouping], options[:limit])
end
- end
-
- def self.get_last_reporting_period(options)
- return ReportingPeriod.new(options[:grouping], options[:end_date]) if options[:end_date]
end
end
end