From edd3644654f5e9ccb55125698ab3c72e058e6159 Mon Sep 17 00:00:00 2001 From: Lee Lawlor Date: Tue, 7 Oct 2014 12:20:49 -0400 Subject: [PATCH] display charts correctly even when invalid min/max values are present --- app/models/channel.rb | 38 ++++++++++++++++++++++++++++++++++ app/views/apps/index.html.erb | 8 ++++++- app/views/charts/show.html.erb | 4 ++-- config/locales/en.yml | 2 ++ spec/models/channel_spec.rb | 29 ++++++++++++++++++++++++++ 5 files changed, 78 insertions(+), 3 deletions(-) diff --git a/app/models/channel.rb b/app/models/channel.rb index 325624c..09c2e5a 100644 --- a/app/models/channel.rb +++ b/app/models/channel.rb @@ -90,6 +90,44 @@ class Channel < ActiveRecord::Base cattr_reader :per_page @@per_page = 15 + # access a last value by string: channel_1417_field_1 + def self.value_from_string(channel_string, user) + # remove % from the string and create the array + channel_array = channel_string.gsub('%', '').split('_') + # exit if the string doesn't have 4 parts + return nil if channel_array.length != 4 + + # get the channel + channel = Channel.find(channel_array[1]) + # exit if the channel is not public or not owned by the user + return nil if !(channel.public_flag? || channel.user_id == user.try(:id)) + # get the field id + field_id = channel_array[3].to_i + + # get the feed + begin + # add a timeout since this query may be really long if there is a lot of data, + # but the last instance of the field is very far back + Timeout.timeout(5, Timeout::Error) do + # look for a feed where the value isn't null + @feed = Feed.where(:channel_id => channel.id) + .where("field? is not null", field_id) + .select("entry_id, field#{field_id}") + .order('entry_id desc') + .first + end + rescue Timeout::Error + rescue + end + + # no feed found + return nil if @feed.blank? + + # return the feed value + return @feed["field#{field_id}"] + end + + # search for public channels within a certain distance from the origin # requires latitude, longitude, and distance to be present as options keys # distance is in kilometers diff --git a/app/views/apps/index.html.erb b/app/views/apps/index.html.erb index 6184166..0ce8d33 100644 --- a/app/views/apps/index.html.erb +++ b/app/views/apps/index.html.erb @@ -96,13 +96,19 @@ <%= t(:help_apps_react) %> -

<%= t(:talkback) %>

<%= t(:help_apps_talkback) %> +

+ +

+ <%= t(:timecontrol) %> + +

+ <%= t(:help_apps_timecontrol) %> diff --git a/app/views/charts/show.html.erb b/app/views/charts/show.html.erb index bc9615d..ea609ca 100644 --- a/app/views/charts/show.html.erb +++ b/app/views/charts/show.html.erb @@ -44,7 +44,7 @@ // add location if possible if (this.location) { p.name = this.location; } // if a numerical value exists add it - if (!isNaN(parseInt(v))<% if params[:max] %> && p.y <= <%= params[:max]%><% end %><% if params[:min] %> && p.y >= <%= params[:min]%><% end %>) { chartData.push(p); } + if (!isNaN(parseInt(v))<% if params[:max].present? && Feed.numeric?(params[:max]) %> && p.y <= <%= params[:max] %><% end %><% if params[:min].present? && Feed.numeric?(params[:max]) %> && p.y >= <%= params[:min] %><% end %>) { chartData.push(p); } }); // specify the chart options @@ -97,7 +97,7 @@ shift = true ; } // if a numerical value exists and it is a new date, add it - if (!isNaN(parseInt(v)) && (p.x != last_date)<% if params[:max] %> && p.y <= <%= params[:max]%><% end %><% if params[:min] %> && p.y >= <%= params[:min]%><% end %>) { + if (!isNaN(parseInt(v)) && (p.x != last_date)<% if params[:max].present? && Feed.numeric?(params[:max]) %> && p.y <= <%= params[:max] %><% end %><% if params[:min].present? && Feed.numeric?(params[:min]) %> && p.y >= <%= params[:min] %><% end %>) { dynamicChart.series[0].addPoint(p, true, shift); } else { diff --git a/config/locales/en.yml b/config/locales/en.yml index b11933a..5c88e6f 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -420,6 +420,7 @@ en: help_apps_talkback: "Allow devices to execute queued commands." help_apps_thinghttp: "Create custom POSTs or GETs to other webservices and retrieve the data." help_apps_thingtweet: "Link your Twitter account to ThingSpeak and send Twitter messages using our simple API." + help_apps_timecontrol: "Automatically perform ThingHTTP requests at predetermined times." help_apps_tweetcontrol: "Listen to commands from Twitter and then perform an action." help_apps_react: "Perform actions when conditions are met by your data in your channels." help_channel: "Create a channel -- it can be for a device, app, or anything that can send data to ThingSpeak." @@ -459,6 +460,7 @@ en: help_thinghttp_show: "You can now send your ThingHTTP request and view the response using the following URL:" help_thingtweet: "ThingTweet acts as a proxy to Twitter so that your devices can update Twitter statuses without having to implement Open Authentication (OAuth)." help_timecontrol_index: "Create a TimeControl to automatically execute ThingHTTP requests at predetermined times." + help_timecontrol_show: "Your TimeControl will be automatically executed at the indicated time." help_tweetcontrol: "Use TweetControl to listen to specific trigger words from Twitter, and then process a ThingHTTP request." help_tweetcontrol_edit: "Select Anonymous TweetControl to allow anyone to trigger your TweetControl or fill in a specfic Twitter Account (don't include the '@' sign)." help_tweetcontrol_hashtag: "To trigger a TweetControl, you need to send a Twitter Status Update with at least the hashtag #thingspeak and the trigger word, for example:" diff --git a/spec/models/channel_spec.rb b/spec/models/channel_spec.rb index a531eb1..0b04862 100644 --- a/spec/models/channel_spec.rb +++ b/spec/models/channel_spec.rb @@ -146,5 +146,34 @@ describe Channel do Channel.location_search({latitude: 30.8, longitude: 30.2, distance: 100}).count.should eq(0) end end + + describe 'value_from_string' do + before :each do + @user = FactoryGirl.create(:user) + @channel = FactoryGirl.create(:channel, public_flag: false, user_id: @user.id) + @feed = FactoryGirl.create(:feed, field1: 7, channel_id: @channel.id) + end + + it 'should get the last value correctly' do + Channel.value_from_string("channel_#{@channel.id}_field_1", @user).should eq('7') + end + + it 'has an incorrect user' do + Channel.value_from_string("channel_#{@channel.id}_field_1", nil).should eq(nil) + end + + it 'has an incorrect user, but channel is public' do + @channel.update_column(:public_flag, true) + Channel.value_from_string("channel_#{@channel.id}_field_1", nil).should eq('7') + end + + it 'has an incorrect string' do + Channel.value_from_string("channel_#{@channel.id}_field", @user).should eq(nil) + end + + it 'has an incorrect field' do + Channel.value_from_string("channel_#{@channel.id}_field_8", @user).should eq(nil) + end + end end