update with changes from Production branch

This commit is contained in:
Lee Lawlor
2014-02-17 12:05:39 -05:00
parent 5b640cf9d8
commit a4937fb2e5
384 changed files with 14690 additions and 2242 deletions

View File

@ -1,14 +1,27 @@
# == Schema Information
#
# Table name: api_keys
#
# id :integer not null, primary key
# api_key :string(16)
# channel_id :integer
# user_id :integer
# write_flag :boolean default(FALSE)
# created_at :datetime
# updated_at :datetime
# note :string(255)
#
class ApiKey < ActiveRecord::Base
belongs_to :channel
belongs_to :user
validates_uniqueness_of :api_key
scope :write_keys, :conditions => { :write_flag => true }
scope :read_keys, :conditions => { :write_flag => false }
scope :write_keys, lambda { where("write_flag = true") }
scope :read_keys, lambda { where("write_flag = false") }
attr_readonly :created_at
attr_accessible :note
def to_s
api_key
@ -22,18 +35,3 @@ end
# == Schema Information
#
# Table name: api_keys
#
# id :integer(4) not null, primary key
# api_key :string(16)
# channel_id :integer(4)
# user_id :integer(4)
# write_flag :boolean(1) default(FALSE)
# created_at :datetime
# updated_at :datetime
# note :string(255)
#

View File

@ -1,18 +1,392 @@
# == Schema Information
#
# Table name: channels
#
# id :integer not null, primary key
# user_id :integer
# name :string(255)
# description :string(255)
# latitude :decimal(15, 10)
# longitude :decimal(15, 10)
# field1 :string(255)
# field2 :string(255)
# field3 :string(255)
# field4 :string(255)
# field5 :string(255)
# field6 :string(255)
# field7 :string(255)
# field8 :string(255)
# scale1 :integer
# scale2 :integer
# scale3 :integer
# scale4 :integer
# scale5 :integer
# scale6 :integer
# scale7 :integer
# scale8 :integer
# created_at :datetime
# updated_at :datetime
# elevation :string(255)
# last_entry_id :integer
# public_flag :boolean default(FALSE)
# options1 :string(255)
# options2 :string(255)
# options3 :string(255)
# options4 :string(255)
# options5 :string(255)
# options6 :string(255)
# options7 :string(255)
# options8 :string(255)
# social :boolean default(FALSE)
# slug :string(255)
# status :string(255)
# url :string(255)
# video_id :string(255)
# video_type :string(255)
# clearing :boolean default(FALSE), not null
# ranking :integer
#
class Channel < ActiveRecord::Base
include KeyUtilities
belongs_to :user
has_many :feeds
has_many :api_keys
has_many :feeds
has_many :daily_feeds
has_many :api_keys, :dependent => :destroy
has_many :taggings
has_many :tags, :through => :taggings
has_many :comments, :dependent => :destroy
has_many :windows, :dependent => :destroy, :autosave => true
self.include_root_in_json = true
attr_readonly :created_at
attr_protected :user_id, :last_entry_id
after_create :set_initial_default_name
before_validation :set_default_name
after_destroy :delete_feeds
validates :name, :presence => true, :on => :update
after_commit :set_default_name
after_commit :set_ranking, :unless => "ranking == calc_ranking"
before_destroy :delete_feeds
validates :video_type, :presence => true, :if => lambda{ |channel| !channel.video_id.nil? && !channel.video_id.empty?}
scope :public_viewable, lambda { where("public_flag = true AND social != true") }
scope :is_public, lambda { where("public_flag = true") }
scope :active, lambda { where("channels.last_entry_id > 1 and channels.updated_at > ?", DateTime.now.utc - 7.day) }
scope :being_cleared, lambda { where("clearing = true") }
scope :by_array, lambda {|ids| { :conditions => ["id in (?)", ids.uniq] } }
scope :with_tag, lambda {|name| joins(:tags).where("tags.name = ?", name) }
# pagination variables
cattr_reader :per_page
@@per_page = 15
# select options
def select_options
only = [:name, :created_at, :updated_at, :id, :last_entry_id]
only += [:description] unless self.description.blank?
only += [:latitude] unless self.latitude.blank?
only += [:longitude] unless self.longitude.blank?
only += [:elevation] unless self.elevation.blank?
only += [:field1] unless self.field1.blank?
only += [:field2] unless self.field2.blank?
only += [:field3] unless self.field3.blank?
only += [:field4] unless self.field4.blank?
only += [:field5] unless self.field5.blank?
only += [:field6] unless self.field6.blank?
only += [:field7] unless self.field7.blank?
only += [:field8] unless self.field8.blank?
# return a hash
return { :only => only }
end
# adds a feed to the channel
def add_status_feed(status)
# update the entry_id for the channel
entry_id = self.next_entry_id
self.last_entry_id = entry_id
self.save
# create the new feed with the correct status and entry_id
self.feeds.create(:status => status, :entry_id => entry_id)
end
# get next last_entry_id for a channel
def next_entry_id
self.last_entry_id.nil? ? 1 : self.last_entry_id + 1
end
# for internal admin use, shows the ids of a channel per month (useful as a proxy for growth)
def show_growth
output = []
date = self.feeds.order("entry_id asc").first.created_at
# while the date is in the past
while (date < Time.now)
# get a feed on that day
feed = self.feeds.where("created_at > ?", date).where("created_at < ?", date + 1.day).first
# output the date and feed id
output << "#{date.strftime('%Y-%m-%d')},#{feed.id}" if feed.present?
# set the date 1 month further
date = date + 1.month
end
# show the output
puts output.join("\n")
end
# paginated hash for json and xml output
# channels input must be paginated
def self.paginated_hash(channels)
{
pagination:
{
current_page: channels.current_page,
per_page: channels.per_page,
total_entries: channels.total_entries,
},
channels: channels.as_json(Channel.public_options)
}
end
# for to_json or to_xml, return only the public attributes
def self.public_options
{
:root => false,
:only => [:id, :name, :description, :latitude, :longitude, :last_entry_id, :elevation, :created_at, :ranking],
:methods => :username,
:include => { :tags => {:only => [:id, :name]}}
}
end
# login name of the user who created the channel
def username; self.user.try(:login); end
# custom as_json method to allow: root => false
def as_json(options = nil)
root = include_root_in_json
root = options[:root] if options.try(:key?, :root)
if root
root = self.class.model_name.element if root == true
{ root => serializable_hash(options) }
else
serializable_hash(options)
end
end
def private_windows *hidden
if hidden.size >= 1
return windows.where("private_flag = true and show_flag = #{hidden[0].to_s}")
else
return windows.where("private_flag = true" )
end
end
# overloaded version witthout private/public flag for the has_many dependent destroy action
def public_windows hidden
return windows.where("private_flag = false and show_flag = #{hidden}")
end
# measure of activity in terms of feeds per time period
def public?
return public_flag
end
def video_changed?
video_id_changed? || video_type_changed?
end
def location_changed?
latitude_changed? || longitude_changed?
end
def feeds_changed?
field1_changed? ||
field2_changed? ||
field3_changed? ||
field4_changed? ||
field5_changed? ||
field6_changed? ||
field7_changed? ||
field8_changed?
end
def update_chart_portlets
self.fields.each do |field|
update_chart_portlet field, true
update_chart_portlet field, false
end
#remove portlets for fields that don't exist
#iterate all chart windows... and look for a matching field
chartWindows = windows.where(:wtype => :chart )
chartWindows.each do |window|
if self.send(window.name).blank?
window.destroy
end
end
end
def update_status_portlet isPrivate
window = windows.where(:wtype => :status, :private_flag => isPrivate )
status_html = "<iframe class=\"statusIFrame\" width=\"450\" height=\"260\" frameborder=\"0\" src=\"/channels/#{id}/status/recent\"></iframe>"
if window.nil? || window[0].nil?
window = PortletWindow.new
window.wtype = :status
window.position = 1
window.col = 1
window.title = "window_status"
else
window = window[0]
end
window.private_flag = isPrivate
window.html = status_html
window.window_detail = PortletWindowDetail.new if window.window_detail.nil?
self.windows.push window
end
def video_fields_valid?
!video_id.nil? && !video_id.empty? && !video_type.nil? && !video_type.empty?
end
def update_video_portlet isPrivate
window = windows.where(:wtype => :video, :private_flag => isPrivate )
if video_fields_valid?
youtube_html = "<iframe class=\"youtube-player\" type=\"text/html\" width=\"452\" height=\"260\" src=\"https://www.youtube.com/embed/#{video_id}?wmode=transparent\" frameborder=\"0\" wmode=\"Opaque\" ></iframe>"
vimeo_html = "<iframe class=\"vimeo-player\" type=\"text/html\" width=\"452\" height=\"260\" src=\"http://player.vimeo.com/video/#{video_id}\" frameborder=\"0\"></iframe>"
if window.nil? || window[0].nil?
window = PortletWindow.new
window.wtype = :video
window.position = 1
window.col = 1
window.title = "window_channel_video"
else
window = window[0]
end
window.private_flag = isPrivate
window.html = youtube_html if video_type == 'youtube'
window.html = vimeo_html if video_type == 'vimeo'
window.window_detail = PortletWindowDetail.new if window.window_detail.nil?
self.windows.push window
else
unless window[0].nil?
window[0].delete
end
end
end
def update_location_portlet isPrivate
window = windows.where(:wtype => :location, :private_flag => isPrivate )
if !latitude.nil? && !longitude.nil?
maps_html = "<iframe width=\"450\" height=\"260\" frameborder=\"0\" scrolling=\"no\" " +
"src=\"/channels/#{id}/maps/channel_show?width=450&height=260\"></iframe>"
if window.nil? || window[0].nil?
window = PortletWindow.new
window.wtype = :location
window.position = 0
window.col = 1
window.title = "window_map"
else
window = window[0]
end
window.private_flag = isPrivate
window.html = maps_html
window.window_detail = PortletWindowDetail.new if window.window_detail.nil?
self.windows.push window
else
unless window[0].nil?
window[0].delete
end
end
end
# get recent status messages from channel
def recent_statuses
self.feeds.select('status, created_at, entry_id').order('created_at DESC').limit(30).collect {|f| f unless f.status.blank? }.compact
end
def latest_feed
self.feeds.where(:entry_id => self.last_entry_id).first
end
def delete_feeds
if self.feeds.count < 1000
Feed.delete_all(["channel_id = ?", self.id])
DailyFeed.delete_all(["channel_id = ?", self.id])
begin
self.update_attribute(:last_entry_id, nil)
rescue Exception => e
end
else
self.update_attribute(:clearing, true)
Resque.enqueue(ClearChannelJob, self.id)
end
end
# true if channel is active
def active?
return (last_entry_id and updated_at and last_entry_id > 1 and updated_at > DateTime.now.utc - 1.days)
end
def list_tags
(self.tags.collect { |t| t.name }).join(', ')
end
def save_tags(tags)
# for each tag
tags.split(',').each do |name|
tag = Tag.find_by_name(name.strip)
# save if new tag
if tag.nil?
tag = Tag.new
tag.name = name.strip
tag.save
end
tagging = Tagging.find(:first, :conditions => { :tag_id => tag.id, :channel_id => self.id})
# save if new tagging
if tagging.nil?
tagging = Tagging.new
tagging.channel_id = self.id
tagging.tag_id = tag.id
tagging.save
end
end
# delete any tags that were removed
self.remove_tags(tags)
end
# if tags don't exist anymore, remove them
def remove_tags(tags)
tag_array = tags.split(',')
# remove white space
tag_array = tag_array.collect {|t| t.strip }
# get all taggings for this channel
taggings = Tagging.find(:all, :conditions => { :channel_id => self.id }, :include => :tag)
# check for existence
taggings.each do |tagging|
# if tagging is not in list
if !tag_array.include?(tagging.tag.name)
# delete tagging
tagging.delete
end
end
end
def add_write_api_key
write_key = self.api_keys.new
@ -22,61 +396,102 @@ class Channel < ActiveRecord::Base
write_key.save
end
def queue_react
self.reacts.on_insertion.each do |react|
begin
Resque.enqueue(ReactJob, react.id)
rescue Exception => e
end
end
end
def field_label(field_number)
self.attributes["field#{field_number}"]
end
def delete_feeds
Feed.delete_all(["channel_id = ?", self.id])
def fields
fields = attribute_names.reject { |x|
!(x.index('field') && self[x] && !self[x].empty?)
}
end
private
def calc_ranking
result = 0
result = result + 15 unless name.blank?
result = result + 20 unless description.blank?
result = result + 15 unless latitude.blank? || longitude.blank?
result = result + 15 unless url.blank?
result = result + 15 unless video_id.blank? || video_type.blank?
def set_default_name
self.name = "#{I18n.t(:channel_default_name)} #{self.id}" if self.name.blank?
result = result + 20 unless tags.empty?
result
end
def set_initial_default_name
update_attribute(:name, "#{I18n.t(:channel_default_name)} #{self.id}")
def set_windows
#check for video window
if video_changed?
update_video_portlet true
update_video_portlet false
end
#does channel have a location and corresponding google map
if location_changed?
update_location_portlet true
update_location_portlet false
end
#does channel have status and corresponding status window. Add the status window no matter what. Only display if it has values
update_status_portlet true
update_status_portlet false
#does channel have a window for every chart element
if feeds_changed?
update_chart_portlets
end
end
private
def set_ranking
update_attribute(:ranking, calc_ranking) unless ranking == calc_ranking
end
def update_chart_portlet (field, isPrivate)
chartWindows = windows.where(:type => "ChartWindow", :name => "field#{field.last.to_s}", :private_flag => isPrivate )
if chartWindows.nil? || chartWindows[0].nil?
window = ChartWindow.new
window.wtype = :chart
window.position = 0
window.col = 0
window.title = "window_field_chart"
window.name = field.to_s
window.window_detail = ChartWindowDetail.new
window.window_detail.options = "&results=60&dynamic=true"
else
window = chartWindows[0]
# If there are options, use them.. if options are not available, then assign defaults
window.window_detail.options ||= "&results=60&dynamic=true"
end
window.window_detail.field_number = field.last
window.private_flag = isPrivate
windows.push window
window.html ="<iframe id=\"iframe#{window.id}\" width=\"450\" height=\"260\" style=\"border: 1px solid #cccccc;\" src=\"/channels/#{id}/charts/#{field.last.to_s}?width=450&height=260::OPTIONS::\" ></iframe>"
if !window.save
raise "The Window could not be saved"
end
end
def set_default_name
update_attribute(:name, "#{I18n.t(:channel_default_name)} #{self.id}") if self.name.blank?
end
end
# == Schema Information
#
# Table name: channels
#
# id :integer(4) not null, primary key
# user_id :integer(4)
# name :string(255)
# description :string(255)
# latitude :decimal(15, 10)
# longitude :decimal(15, 10)
# field1 :text
# field2 :text
# field3 :text
# field4 :text
# field5 :text
# field6 :text
# field7 :text
# field8 :text
# scale1 :integer(4)
# scale2 :integer(4)
# scale3 :integer(4)
# scale4 :integer(4)
# scale5 :integer(4)
# scale6 :integer(4)
# scale7 :integer(4)
# scale8 :integer(4)
# created_at :datetime
# updated_at :datetime
# elevation :string(255)
# last_entry_id :integer(4)
# public_flag :boolean(1) default(FALSE)
#

8
app/models/chart.rb Normal file
View File

@ -0,0 +1,8 @@
class Chart
def self.default_width
450
end
def self.default_height
250
end
end

View File

@ -0,0 +1,22 @@
# == Schema Information
#
# Table name: windows
#
# id :integer not null, primary key
# channel_id :integer
# position :integer
# created_at :datetime
# updated_at :datetime
# html :text
# col :integer
# title :string(255)
# wtype :string(255)
# name :string(255)
# type :string(255)
# private_flag :boolean default(FALSE)
# show_flag :boolean default(TRUE)
#
class ChartWindow < Window
relate_to_details
end

View File

@ -0,0 +1,14 @@
# == Schema Information
#
# Table name: chart_window_details
#
# id :integer not null, primary key
# chart_window_id :integer
# field_number :integer
# created_at :datetime
# updated_at :datetime
# options :string(255)
#
class ChartWindowDetail < ActiveRecord::Base
end

32
app/models/comment.rb Normal file
View File

@ -0,0 +1,32 @@
# == Schema Information
#
# Table name: comments
#
# id :integer not null, primary key
# parent_id :integer
# body :text
# flags :integer
# user_id :integer
# ip_address :string(255)
# created_at :datetime
# updated_at :datetime
# channel_id :integer
#
class Comment < ActiveRecord::Base
belongs_to :channel
belongs_to :user
acts_as_tree :order => 'created_at'
validates :body, :presence => true
validates_associated :user
before_create :set_defaults
private
def set_defaults
self.flags = 0
end
end

47
app/models/daily_feed.rb Normal file
View File

@ -0,0 +1,47 @@
# == Schema Information
#
# Table name: daily_feeds
#
# id :integer not null, primary key
# channel_id :integer
# date :date
# calculation :string(20)
# result :string(255)
# field :integer
#
class DailyFeed < ActiveRecord::Base
belongs_to :channel
self.include_root_in_json = false
# update a feed if it exists, or else create it
def self.my_create_or_update(attributes)
# try to get daily feed
daily_feed = DailyFeed.where(attributes).first
# if there is an existing daily feed
if daily_feed.present?
# update it
daily_feed.update_attributes(attributes)
# else create it
else
daily_feed = DailyFeed.create(attributes)
end
end
# gets the calculation type
def self.calculation_type(params)
output = nil
output = 'timescale' if params[:timescale].present?
output = 'sum' if params[:sum].present?
output = 'average' if params[:average].present?
output = 'median' if params[:median].present?
return output
end
# checks to see if this is a daily feed
def self.valid_params(params)
(params[:timescale] == '1440' || params[:sum] == '1440' || params[:average] == '1440' || params[:median] == '1440') ? true : false
end
end

View File

@ -0,0 +1,41 @@
class ErrorResponse
def initialize(error_code)
error_object = I18n.t(:error_codes)[error_code]
@error_code = error_code.to_s
@http_status = error_object[:http_status]
@message = error_object[:message]
@details = error_object[:details]
end
# attributes that can be read
attr_reader :error_code, :http_status, :message, :details
# custom json format
def as_json(options = nil)
{
:status => "#{http_status}",
:error => {
:error_code => error_code,
:message => message,
:details => details
}
}
end
# custom xml format
def to_xml
output = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
output += "<hash>\n"
output += " <status>#{http_status}</status>\n"
output += " <error>\n"
output += " <error-code>#{error_code}</error-code>\n"
output += " <message>#{message}</message>\n"
output += " <details>#{details}</details>\n"
output += " </error>\n"
output += "</hash>"
return output
end
end

14
app/models/failedlogin.rb Normal file
View File

@ -0,0 +1,14 @@
# == Schema Information
#
# Table name: failedlogins
#
# id :integer not null, primary key
# login :string(255)
# password :string(255)
# ip_address :string(255)
# created_at :datetime
# updated_at :datetime
#
class Failedlogin < ActiveRecord::Base
end

View File

@ -1,33 +1,137 @@
class Feed < ActiveRecord::Base
belongs_to :channel
self.include_root_in_json = false
attr_readonly :created_at
attr_protected :channel_id
end
# == Schema Information
#
# Table name: feeds
#
# id :integer(4) not null, primary key
# channel_id :integer(4)
# raw_data :text
# field1 :text
# field2 :text
# field3 :text
# field4 :text
# field5 :text
# field6 :text
# field7 :text
# field8 :text
# id :integer not null, primary key
# channel_id :integer
# field1 :string(255)
# field2 :string(255)
# field3 :string(255)
# field4 :string(255)
# field5 :string(255)
# field6 :string(255)
# field7 :string(255)
# field8 :string(255)
# created_at :datetime
# updated_at :datetime
# entry_id :integer(4)
# entry_id :integer
# status :string(255)
# latitude :decimal(15, 10)
# longitude :decimal(15, 10)
# elevation :string(255)
# location :string(255)
#
class Feed < ActiveRecord::Base
extend FeedHelper
belongs_to :channel
after_commit :queue_react
delegate :queue_react, :to => :channel
self.include_root_in_json = false
attr_readonly :created_at
# only output these fields for feed
def self.select_options(channel, params)
only = [:created_at]
only += [:entry_id] unless timeparam_valid?(params[:timescale]) or timeparam_valid?(params[:average]) or timeparam_valid?(params[:median]) or timeparam_valid?(params[:sum])
only += [:field1] unless channel.field1.blank? or (params[:field_id] and !params[:field_id].index('1'))
only += [:field2] unless channel.field2.blank? or (params[:field_id] and !params[:field_id].index('2'))
only += [:field3] unless channel.field3.blank? or (params[:field_id] and !params[:field_id].index('3'))
only += [:field4] unless channel.field4.blank? or (params[:field_id] and !params[:field_id].index('4'))
only += [:field5] unless channel.field5.blank? or (params[:field_id] and !params[:field_id].index('5'))
only += [:field6] unless channel.field6.blank? or (params[:field_id] and !params[:field_id].index('6'))
only += [:field7] unless channel.field7.blank? or (params[:field_id] and !params[:field_id].index('7'))
only += [:field8] unless channel.field8.blank? or (params[:field_id] and !params[:field_id].index('8'))
# add geolocation data if necessary
if params[:location] and params[:location].upcase == 'TRUE'
only += [:latitude]
only += [:longitude]
only += [:elevation]
only += [:location]
end
# add status if necessary
only += [:status] if params[:status] and params[:status].upcase == 'TRUE'
return only
end
# outputs feed info correctly, used by daily_feeds
def self.normalize_feeds(daily_feeds)
output = []
hash = {}
# for each daily feed
daily_feeds.each do |daily_feed|
# check if the feed already exists
existing_feed = hash[daily_feed['date']]
# skip blank feeds
next if daily_feed['date'].blank?
# if the feed exists
if existing_feed.present?
# add the new field
existing_feed["field#{daily_feed['field']}"] = daily_feed['result']
# else add a new feed
else
new_feed = Feed.new(:created_at => daily_feed['date'])
# set the field attribute correctly
new_feed["field#{daily_feed['field']}"] = daily_feed['result']
# add the feed
hash[daily_feed['date']] = new_feed
end
end
# turn the hash into an array
output = hash.values
# sort by date
return output
end
# custom json output
def as_json(options = {})
super(options.merge(:except => [:updated_at, :id]))
end
# check if a field value is a number
# usage: Feed.numeric?(field_value)
def self.numeric?(object)
true if Float(object) rescue false
end
def field(number)
self.attributes["field#{number.to_i}"]
end
# make sure any selected fields are greater than a minimum
def greater_than?(minimum)
output = true
self.attributes.each do |attribute|
# if this attribute is a numeric field with a value
if attribute[0].to_s.index('field') == 0 && attribute[1].present? && Feed.numeric?(attribute[1])
output = false if attribute[1].to_f < minimum.to_f
end
end
return output
end
# make sure any selected fields are less than a minimum
def less_than?(maximum)
output = true
self.attributes.each do |attribute|
# if this attribute is a numeric field with a value
if attribute[0].to_s.index('field') == 0 && attribute[1].present? && Feed.numeric?(attribute[1])
output = false if attribute[1].to_f > maximum.to_f
end
end
return output
end
end

197
app/models/feed_factory.rb Normal file
View File

@ -0,0 +1,197 @@
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
# 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?
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 feed based on conditions
@feeds = Feed.where(:channel_id => @channel.id, :created_at => @date_range)
.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

16
app/models/header.rb Normal file
View File

@ -0,0 +1,16 @@
# == Schema Information
#
# Table name: headers
#
# id :integer not null, primary key
# name :string(255)
# value :string(255)
# created_at :datetime
# updated_at :datetime
# thinghttp_id :integer
#
class Header < ActiveRecord::Base
belongs_to :thinghttp
end

View File

@ -1,11 +1,11 @@
class Mailer < ActionMailer::Base
#default :from => 'support@thingspeak.com'
default :from => 'support@thingspeak.com'
def password_reset(user, webpage)
@user = user
@webpage = webpage
mail(:to => @user.email,
:subject => t(:password_reset_subject))
end
@user = user
@webpage = webpage
mail(:to => @user.email,
:subject => t(:password_reset_subject))
end
end

22
app/models/pipe.rb Normal file
View File

@ -0,0 +1,22 @@
# == Schema Information
#
# Table name: pipes
#
# id :integer not null, primary key
# name :string(255) not null
# url :string(255) not null
# slug :string(255) not null
# created_at :datetime
# updated_at :datetime
# parse :string(255)
# cache :integer
#
class Pipe < ActiveRecord::Base
# pagination variables
cattr_reader :per_page
@@per_page = 50
end

134
app/models/plugin.rb Normal file
View File

@ -0,0 +1,134 @@
# == Schema Information
#
# Table name: plugins
#
# id :integer not null, primary key
# name :string(255)
# user_id :integer
# html :text
# css :text
# js :text
# created_at :datetime
# updated_at :datetime
# private_flag :boolean default(TRUE)
#
class Plugin < ActiveRecord::Base
belongs_to :user
has_many :plugin_window_details
has_many :windows, :through => :plugin_window_details, :source => :plugin_window
before_destroy { |record| record.windows.each { |window| window.delete } }
def destroy_window
window_id = PluginWindowDetail.find_by_plugin_id(self.id).plugin_window_id
Window.delete(window_id)
end
def private?
private_flag
end
def public?
!private_flag
end
def has_private_windows(channel_id)
has_private_windows = false
windows.each do |window|
if window.private? && window.channel_id == channel_id
has_private_windows = true
end
end
return has_private_windows
end
def has_public_windows(channel_id)
has_public_windows = false
windows.each do |window|
has_public_windows = true if !window.private? && window.channel_id == channel_id
end
return has_public_windows
end
#private_dashboard_visibility
def private_dashboard_windows(channel_id)
dashboard_windows channel_id, true
end
def public_dashboard_windows(channel_id)
dashboard_windows channel_id, false
end
def dashboard_windows(channel_id, privacy)
dashboard_windows = []
windows.each do |window|
if window.private_flag == privacy && !window.show_flag && channel_id == window.channel_id
dashboard_windows << window
end
end
dashboard_windows
end
#public_dashboard_visibility
def public_window
public_window = nil
windows.each do |window|
if !window.private_flag # && !window.show_flag
public_window = window
end
end
unless public_window.nil?
public_window
else
nil
end
end
def make_windows(channel_id, api_domain)
pluginWindows = []
#create all the windows as appropriate
#Private plugins have one window..
#Public plugins have a private/private windows, private/public window and a public window
if !has_public_windows(channel_id) && self.public?
windows << PluginWindow.new_from(self, channel_id, :public, api_domain)
else
update_windows(channel_id)
end
if !has_private_windows(channel_id)
windows << Window.new_from(self, channel_id, :private, api_domain)
end
save
end
def update_windows(channel_id)
windows.each do |window|
window.name = self.name
window.save
end
if has_public_windows(channel_id) && self.private?
windows.delete(public_window.destroy) unless public_window.nil?
end
end
def update_all_windows
channel_ids = Set.new
windows.each do |window|
window.name = self.name
channel_ids.add( window.channel_id)
window.save
end
channel_ids.each do |id|
if has_public_windows(id) && self.private?
windows.delete(public_window.destroy) unless public_window.nil?
end
end
end
end

View File

@ -0,0 +1,23 @@
# == Schema Information
#
# Table name: windows
#
# id :integer not null, primary key
# channel_id :integer
# position :integer
# created_at :datetime
# updated_at :datetime
# html :text
# col :integer
# title :string(255)
# wtype :string(255)
# name :string(255)
# type :string(255)
# private_flag :boolean default(FALSE)
# show_flag :boolean default(TRUE)
#
class PluginWindow < Window
relate_to_details
end

View File

@ -0,0 +1,16 @@
# == Schema Information
#
# Table name: plugin_window_details
#
# id :integer not null, primary key
# plugin_id :integer
# plugin_window_id :integer
# created_at :datetime
# updated_at :datetime
#
class PluginWindowDetail < ActiveRecord::Base
belongs_to :plugin_window
belongs_to :plugin
end

View File

@ -0,0 +1,22 @@
# == Schema Information
#
# Table name: windows
#
# id :integer not null, primary key
# channel_id :integer
# position :integer
# created_at :datetime
# updated_at :datetime
# html :text
# col :integer
# title :string(255)
# wtype :string(255)
# name :string(255)
# type :string(255)
# private_flag :boolean default(FALSE)
# show_flag :boolean default(TRUE)
#
class PortletWindow < Window
relate_to_details
end

View File

@ -0,0 +1,12 @@
# == Schema Information
#
# Table name: portlet_window_details
#
# id :integer not null, primary key
# portlet_window_id :integer
# created_at :datetime
# updated_at :datetime
#
class PortletWindowDetail < ActiveRecord::Base
end

19
app/models/tag.rb Normal file
View File

@ -0,0 +1,19 @@
# == Schema Information
#
# Table name: tags
#
# id :integer not null, primary key
# name :string(255)
# created_at :datetime
# updated_at :datetime
#
class Tag < ActiveRecord::Base
has_many :taggings
has_many :channels, :through => :taggings
validates_presence_of :name
self.include_root_in_json = false
end

15
app/models/tagging.rb Normal file
View File

@ -0,0 +1,15 @@
# == Schema Information
#
# Table name: taggings
#
# id :integer not null, primary key
# tag_id :integer
# channel_id :integer
# created_at :datetime
# updated_at :datetime
#
class Tagging < ActiveRecord::Base
belongs_to :tag
belongs_to :channel
end

View File

@ -0,0 +1,57 @@
# == Schema Information
#
# Table name: twitter_accounts
#
# id :integer not null, primary key
# screen_name :string(255)
# user_id :integer
# twitter_id :integer
# token :string(255)
# secret :string(255)
# created_at :datetime
# updated_at :datetime
# api_key :string(17) not null
#
class TwitterAccount < ActiveRecord::Base
include KeyUtilities
belongs_to :user
has_many :reacts, :as => :actionable, :dependent => :restrict_with_exception
# pagination variables
cattr_reader :per_page
@@per_page = 50
before_create :set_api_key
def renew_api_key
self.update_attribute(:api_key, generate_api_key(16, 'twitter'))
end
def tweet(status, opts = {})
opts.delete('api_key')
opts.delete('controller')
opts.delete('action')
client = TwitterOAuth::Client.new(
:consumer_key => CONSUMER_KEY,
:consumer_secret => CONSUMER_SECRET,
:token => self.token,
:secret => self.secret
)
client.update(status, opts)
rescue Twitter::Error::Unauthorized
end
private
def set_api_key
self.api_key = generate_api_key(16, 'twitter')
end
end

View File

@ -1,26 +1,14 @@
class User < ActiveRecord::Base
has_many :channels
has_many :api_keys
acts_as_authentic
def self.find_by_login_or_email(login)
User.find_by_login(login) || User.find_by_email(login)
end
end
# == Schema Information
#
# Table name: users
#
# id :integer(4) not null, primary key
# login :string(255) not null
# email :string(255) not null
# crypted_password :string(255) not null
# password_salt :string(255) not null
# persistence_token :string(255) not null
# perishable_token :string(255) not null
# id :integer not null, primary key
# login :string(255) not null
# email :string(255) not null
# crypted_password :string(255) not null
# password_salt :string(255) not null
# persistence_token :string(255) not null
# perishable_token :string(255) not null
# current_login_at :datetime
# last_login_at :datetime
# current_login_ip :string(255)
@ -28,5 +16,78 @@ end
# created_at :datetime
# updated_at :datetime
# time_zone :string(255)
# public_flag :boolean default(FALSE)
# bio :text
# website :string(255)
# api_key :string(16)
#
####### NOTE #######
# user.api_keys is a collection of channel api_keys (read and write)
# user.api_key is a single api_key that allows control of a user's account
####################
class User < ActiveRecord::Base
include KeyUtilities
has_many :channels
has_many :twitter_accounts, :dependent => :destroy
has_many :thinghttps, :dependent => :destroy
has_many :tweetcontrols, :dependent => :destroy
has_many :reacts, :dependent => :destroy
has_many :scheduled_thinghttps, :dependent => :destroy
has_many :talkbacks, :dependent => :destroy
has_many :plugins
has_many :devices
has_many :api_keys
has_many :watchings, :dependent => :destroy
has_many :watched_channels, :through => :watchings, :source => :channel
has_many :comments
acts_as_authentic
self.include_root_in_json = false
# pagination variables
cattr_reader :per_page
@@per_page = 50
# find a user using login or email
def self.find_by_login_or_email(login)
User.find_by_login(login) || User.find_by_email(login)
end
# get user signups per day
def self.signups_per_day
sql = 'select DATE_FORMAT(created_at,"%Y-%m-%d") as day, count(id) as signups from users group by day'
days = ActiveRecord::Base.connection.execute(sql)
return days
end
# for to_json or to_xml, return only the public attributes
def self.public_options(user)
output = { :only => [:id, :login, :created_at] }
# if the profile is public
if user.public_flag == true
additional_options = { :only => [:website, :bio] }
# merge in the additional options by adding the values
output.merge!(additional_options){ |key, oldval, newval| oldval + newval }
end
return output
end
# for to_json or to_xml, return the correct private attributes
def self.private_options
{ :only => [:id, :login, :created_at, :email, :website, :bio] }
end
# set new api key
def set_new_api_key!
new_api_key = generate_api_key(16, 'user')
self.update_column(:api_key, new_api_key)
return new_api_key
end
end

View File

@ -1,7 +1,7 @@
class UserSession < Authlogic::Session::Base
find_by_login_method :find_by_login_or_email
find_by_login_method :find_by_login_or_email
def to_key
new_record? ? nil : [ self.send(self.class.primary_key) ]
end
end
end

22
app/models/watching.rb Normal file
View File

@ -0,0 +1,22 @@
# == Schema Information
#
# Table name: watchings
#
# id :integer not null, primary key
# user_id :integer
# channel_id :integer
# created_at :datetime
# updated_at :datetime
#
class Watching < ActiveRecord::Base
belongs_to :user
belongs_to :channel
# check if the channel is being watched by this user
def self.check(user_id, channel_id)
@watching = Watching.find_by_user_id_and_channel_id(user_id, channel_id)
return @watching.nil? ? false : true
end
end

50
app/models/window.rb Normal file
View File

@ -0,0 +1,50 @@
# == Schema Information
#
# Table name: windows
#
# id :integer not null, primary key
# channel_id :integer
# position :integer
# created_at :datetime
# updated_at :datetime
# html :text
# col :integer
# title :string(255)
# wtype :string(255)
# name :string(255)
# type :string(255)
# private_flag :boolean default(FALSE)
# show_flag :boolean default(TRUE)
#
class Window < ActiveRecord::Base
belongs_to :channel
self.include_root_in_json = true
def self.relate_to_details
class_eval <<-EOF
has_one :window_detail, :class_name => "#{self.name}Detail"
accepts_nested_attributes_for :window_detail
default_scope { includes(:window_detail) }
EOF
end
def private?
return private_flag
end
def self.new_from( plugin, channel_id, privacy_flag, api_domain )
window = PluginWindow.new
window.wtype = :plugin
window.position = 0
window.col = 0
window.title = "window_plugin"
window.name = plugin.name
window.private_flag = (privacy_flag == :private)
window.channel = Channel.find(channel_id)
window.html ="<iframe width=\"450\" height=\"260\" style=\"border: 1px solid #cccccc;\" src=\"/plugins/#{plugin.id}\" ></iframe>"
window.show_flag = false
window if window.save
end
end