thingspeak/app/models/feed_factory.rb
2014-12-03 10:50:25 -05:00

216 lines
7.2 KiB
Ruby

class FeedFactory < ApplicationController
include FeedHelper
def initialize(options = {})
@options = options # alias for params
@feeds = nil
@daily_feeds = nil
@output_feeds = nil
@rounded = false
@channel = Channel.find(options[:channel_id])
@date_range = get_date_range(options)
@daily_date_range = @date_range.first.to_date..@date_range.last.to_date
@limit = calculate_limit
@use_daily_feed = DailyFeed.valid_params(options) # determine whether daily feed should be used
@calculation_type = DailyFeed.calculation_type(options) # set the calculation type
@feed_select_options = Feed.select_options(@channel, @options)
@cache_feeds = cache_feeds?
end
# attributes that can be read
attr_reader :feeds, :daily_feeds, :limit, :use_daily_feed, :feed_select_options, :cache_feeds, :channel
# range for entry_ids
def entry_id_range
# set start and end id
start_id = @options[:start_entry_id].present? ? @options[:start_entry_id].to_i : 1
end_id = @options[:end_entry_id].present? ? @options[:end_entry_id].to_i : @channel.last_entry_id
# return the range
return start_id..end_id
end
# calculate the limit that should be used
def calculate_limit
limit = 100
limit = 8000 if @options[:results].present? || @options[:days].present? || @options[:start].present? || @options[:end].present? || @options[:start_entry_id] || @options[:end_entry_id]
limit = @options[:results].to_i if (@options[:results].present? && @options[:results].to_i < 8000)
return limit
end
# determine if data should be cached
def cache_feeds?
cache = false
cache = true if (@channel.last_entry_id.present? && @channel.last_entry_id > 100 && @limit > 100)
cache = true if @options[:days].present?
return cache
end
# if daily feeds exist, use that first, or else use regular feeds
def get_output_feeds
# get daily feeds
get_daily_feeds if @use_daily_feed == true
# get feeds normally if no daily feeds
get_feeds if @daily_feeds.blank?
# set minimum and maximum parameters, and round output feeds
format_output_feeds
return @output_feeds
end
# get feed for a date
def get_feed_data_for_date(date)
# get feeds for this date
feeds = Feed.where(:channel_id => @channel.id, :created_at => date..(date + 1.day))
.select(@feed_select_options).order('created_at asc').load
# calculate the feed
feed = calculate_feeds(feeds).first
# add blank feed for this date if necessary
feed = Feed.new(:created_at => date) if feed.nil?
return feed
end
# add a daily feed for a specific date
def add_daily_feed_for_date(date)
# get the feed data
feed = get_feed_data_for_date(date)
# for each attribute
@feed_select_options.each do |attr|
key = attr.to_s
# if this attribute is a field
if key.index('field') == 0
# get the field number
field_number = key.sub('field', '').to_i
# add the feed; replace with Rails 4 create_or_update if appropriate
DailyFeed.my_create_or_update({:channel_id => @channel.id, :date => feed.created_at, :calculation => @calculation_type, :field => field_number, :result => feed[key]})
end
end
# add to existing daily feeds
@daily_feeds << feed
end
# get feeds
def get_feeds
# get feeds based on entry ids
if @options[:start_entry_id].present? || @options[:end_entry_id].present?
@feeds = Feed.from("feeds FORCE INDEX (index_feeds_on_channel_id_and_entry_id)")
.where(:channel_id => @channel.id, :entry_id => entry_id_range)
# get feed based on conditions
else
@feeds = Feed.from("feeds FORCE INDEX (index_feeds_on_channel_id_and_created_at)")
.where(:channel_id => @channel.id, :created_at => @date_range)
end
# apply filters and load the feeds
@feeds = @feeds.select(@feed_select_options)
.order('created_at desc')
.limit(@limit)
.load
# sort properly
@feeds.reverse!
# calculate feeds
@feeds = calculate_feeds(@feeds)
end
# gets daily feeds
def get_daily_feeds
sql_date_range = (@daily_date_range.first + 1.day)..(@daily_date_range.last + 1.day)
# if this is for a specific field
if @options[:field_id].present?
@daily_feeds = DailyFeed.where(:channel_id => @channel.id, :calculation => @calculation_type, :date => sql_date_range, :field => @options[:field_id]).order('date desc').load
# else get daily feeds for all fields
else
@daily_feeds = DailyFeed.where(:channel_id => @channel.id, :calculation => @calculation_type, :date => sql_date_range).order('date desc').load
end
# normalize if there are daily feeds
@daily_feeds = Feed.normalize_feeds(@daily_feeds) if @daily_feeds.present?
# get dates that are missing from daily feed
add_missing_daily_feeds
# add todays data
add_daily_feed_for_today
# sort correctly
@daily_feeds.sort!{ |x, y| x.created_at <=> y.created_at }
end
# add feed data for today
def add_daily_feed_for_today
@daily_feeds << get_feed_data_for_date(Time.now.to_date) if @options[:days].present?
end
# get dates that are missing from daily feed
def add_missing_daily_feeds
missing_dates = []
current_date = @daily_date_range.first + 1.day
# if current date is older than channel date, set it to channel date
current_date = @channel.created_at.to_date if @date_range.first < @channel.created_at
end_date = @daily_date_range.last
# get dates that exist in daily feeds
daily_feed_dates = {}
@daily_feeds.each { |feed| daily_feed_dates[feed.created_at.to_date] = true }
# iterate through each date
while current_date < end_date
# add missing dates
missing_dates << current_date if daily_feed_dates[current_date] != true
# go to the next day
current_date += 1.day
end
# add daily feeds for any missing days
missing_dates.each { |date| add_daily_feed_for_date(date) }
end
# apply rounding and min/max
def format_output_feeds
# set output feeds
@output_feeds = (@daily_feeds.present? ? @daily_feeds : @feeds)
# only get feeds that match min and max values
@output_feeds = @output_feeds.select{ |x| x.greater_than?(@options[:min]) } if @options[:min].present?
@output_feeds = @output_feeds.select{ |x| x.less_than?(@options[:max]) } if @options[:max].present?
# round feeds if necessary
@output_feeds = object_round(@output_feeds, @options[:round].to_i) if @options[:round] && !@rounded
end
# calculate feeds
def calculate_feeds(feeds)
# if a feed has data
if feeds.present?
# convert to timescales if necessary
if timeparam_valid?(@options[:timescale])
feeds = feeds_into_timescales(feeds, @options)
# convert to sums if necessary
elsif timeparam_valid?(@options[:sum])
feeds = feeds_into_sums(feeds, @options)
@rounded = true
# convert to averages if necessary
elsif timeparam_valid?(@options[:average])
feeds = feeds_into_averages(feeds, @options)
@rounded = true
# convert to medians if necessary
elsif timeparam_valid?(@options[:median])
feeds = feeds_into_medians(feeds, @options)
@rounded = true
end
end
return feeds
end
end