Use select2 for type selection; switch to showing JSON for event information

This commit is contained in:
Andrew Cantino 2013-08-08 12:30:17 -07:00
parent 306237c306
commit 86b5b1f203
23 changed files with 373 additions and 402 deletions

View file

@ -49,7 +49,7 @@ showEventDescriptions = ->
$(document).ready ->
setupJsonEditor()
$(".multi-select").select2(width: 'resolve')
$(".select2").select2(width: 'resolve')
if $(".top-flash").length
setTimeout((-> $(".top-flash").slideUp(-> $(".top-flash").remove())), 5000)
@ -58,8 +58,8 @@ $(document).ready ->
$("#agent_type").on "change", ->
if window.jsonEditor?
$(@).closest(".control-group").find(".spinner").fadeIn();
$("#agent_source_ids ").select2("val", {});
$(".spinner").fadeIn();
$("#agent_source_ids").select2("val", {});
$(".event-descriptions").html("").hide()
$.getJSON "/agents/type_details", { type: $(@).val() }, (json) =>
if json.can_be_scheduled
@ -77,7 +77,7 @@ $(document).ready ->
window.jsonEditor.json = json.options
window.jsonEditor.rebuild()
$(@).closest(".control-group").find(".spinner").stop(true, true).fadeOut();
$(".spinner").stop(true, true).fadeOut();
$("#agent_type").change() if $("#agent_type").length

View file

@ -46,7 +46,7 @@ table.events {
}
}
.multi-select {
.select2 {
float: none !important;
margin-left: 0 !important;
}
@ -65,13 +65,8 @@ img.odin {
display: none;
}
.type-select {
width: 275px;
img.spinner {
display: none;
float: right;
}
img.spinner {
display: none;
}
.hidden {

View file

@ -14,13 +14,17 @@ module Agents
event_description <<-MD
If flights are present then events look like:
{ "cost" : 75.23,
"date" : "June 25, 2013",
"route" : "New York to Chicago" }
{
"cost": 75.23,
"date": "June 25, 2013",
"route": "New York to Chicago"
}
otherwise
{ "nonetodest" : "No flights found to the specified destination" }
{
"nonetodest": "No flights found to the specified destination"
}
MD
def default_options

View file

@ -44,9 +44,7 @@ module Agents
}
MD
event_description <<-MD
User defined
MD
event_description "User defined"
def validate_options
errors.add(:base, "instructions, mode, skip_agent, and skip_created_at all need to be present.") unless options[:instructions].present? and options[:mode].present? and options[:skip_agent].present? and options[:skip_created_at].present?

View file

@ -17,9 +17,14 @@ module Agents
MD
event_description <<-MD
Events look like this:
Events look like:
{ :message => "Your message", :peak => 6, :peak_time => 3456789242, :grouped_by => "something" }
{
"message": "Your message",
"peak": 6,
"peak_time": 3456789242,
"grouped_by": "something"
}
MD
def validate_options
@ -69,13 +74,13 @@ module Agents
if newest_value < second_newest_value && second_newest_value > average_value + std_multiple * standard_deviation
memory[:peaks][group] << second_newest_time
memory[:peaks][group].reject! { |p| p <= second_newest_time - window_duration }
create_event :payload => { :message => options[:message], :peak => second_newest_value, :peak_time => second_newest_time, :grouped_by => group.to_s }
create_event :payload => {:message => options[:message], :peak => second_newest_value, :peak_time => second_newest_time, :grouped_by => group.to_s}
end
end
end
def stats_for(group, options = {})
data = memory[:data][group].map {|d| d.first.to_f }
data = memory[:data][group].map { |d| d.first.to_f }
data = data[0...(memory[:data][group].length - (options[:skip_last] || 0))]
length = data.length.to_f
mean = 0

View file

@ -1,43 +1,41 @@
module Agents
class PostAgent < Agent
cannot_be_scheduled!
class PostAgent < Agent
cannot_be_scheduled!
description <<-MD
Post Agent receives events from other agents and send those events as the contents of a post request to a specified url. `post_url` field must specify where you would like to receive post requests and do not forget to include URI scheme(`http` or `https`)
MD
description <<-MD
Post Agent receives events from other agents and send those events as the contents of a post request to a specified url. `post_url` field must specify where you would like to receive post requests and do not forget to include URI scheme (`http` or `https`)
MD
event_description <<-MD
Does not produce any event.
MD
event_description "Does not produce events."
def default_options
{
:post_url => "http://www.example.com",
:expected_receive_period_in_days => 1
}
end
def working?
last_receive_at && last_receive_at > options[:expected_receive_period_in_days].to_i.days.ago
end
def validate_options
unless options[:post_url].present? && options[:expected_receive_period_in_days].present?
errors.add(:base, "post_url and expected_receive_period_in_days are required fields")
end
end
def post_event(uri,event)
req = Net::HTTP::Post.new(uri.request_uri)
req.form_data = event
Net::HTTP.start(uri.hostname, uri.port, :use_ssl => uri.scheme == "https") { |http| http.request(req) }
end
def receive(incoming_events)
incoming_events.each do |event|
uri = URI options[:post_url]
post_event uri, event.payload
end
end
def default_options
{
:post_url => "http://www.example.com",
:expected_receive_period_in_days => 1
}
end
def working?
last_receive_at && last_receive_at > options[:expected_receive_period_in_days].to_i.days.ago
end
def validate_options
unless options[:post_url].present? && options[:expected_receive_period_in_days].present?
errors.add(:base, "post_url and expected_receive_period_in_days are required fields")
end
end
def post_event(uri, event)
req = Net::HTTP::Post.new(uri.request_uri)
req.form_data = event
Net::HTTP.start(uri.hostname, uri.port, :use_ssl => uri.scheme == "https") { |http| http.request(req) }
end
def receive(incoming_events)
incoming_events.each do |event|
uri = URI options[:post_url]
post_event uri, event.payload
end
end
end
end

View file

@ -1,84 +1,85 @@
require 'csv'
module Agents
class SentimentAgent < Agent
class_attribute :anew
class SentimentAgent < Agent
class_attribute :anew
cannot_be_scheduled!
cannot_be_scheduled!
description <<-MD
The SentimentAgent generates `good-bad` (psychological valence or happiness index), `active-passive` (arousal),
and `strong-weak` (dominance) score. It will output a value between 1 and 9. It will only work on English content.
description <<-MD
The SentimentAgent generates `good-bad` (psychological valence or happiness index), `active-passive` (arousal),
and `strong-weak` (dominance) score. It will output a value between 1 and 9. It will only work on English content.
Make sure the content this agent is analyzing have sufficient length to get respectable results.
Make sure the content this agent is analyzing have sufficient length to get respectable results.
Provide a JSONPath in `content` field where content is residing and set `expected_receive_period_in_days` to the maximum number of days you would allow to be passed between events being received by this agent.
MD
Provide a JSONPath in `content` field where content is residing and set `expected_receive_period_in_days` to the maximum number of days you would allow to be passed between events being received by this agent.
MD
event_description <<-MD
Events look like:
{
:content => "The quick brown fox jumps over the lazy dog.",
:valence => 6.196666666666666,
:arousal => 4.993333333333333,
:dominance => 5.63
}
MD
event_description <<-MD
Events look like:
def default_options
{
:content => "$.message.text[*]",
:expected_receive_period_in_days => 1
}
end
{
"content": "The quick brown fox jumps over the lazy dog.",
"valence": 6.196666666666666,
"arousal": 4.993333333333333,
"dominance": 5.63
}
MD
def working?
last_receive_at && last_receive_at > options[:expected_receive_period_in_days].to_i.days.ago
end
def receive(incoming_events)
anew = self.class.sentiment_hash
incoming_events.each do |event|
Utils.values_at(event.payload, options[:content]).each do |content|
sent_values = sentiment_values anew, content
create_event :payload => {:content => content,
:valence => sent_values[0],
:arousal => sent_values[1],
:dominance => sent_values[2],
:original_event => event.payload}
end
end
end
def validate_options
errors.add(:base, "content and expected_receive_period_in_days must be present") unless options[:content].present? && options[:expected_receive_period_in_days].present?
end
def self.sentiment_hash
unless self.anew
self.anew = {}
CSV.foreach Rails.root.join('data/anew.csv') do |row|
self.anew[row[0]] = row.values_at(2,4,6).map {|val| val.to_f}
end
end
self.anew
end
def sentiment_values(anew,text)
valence, arousal, dominance, freq = [0] * 4
text.downcase.strip.gsub(/[^a-z ]/,"").split.each do |word|
if anew.has_key? word
valence += anew[word][0]
arousal += anew[word][1]
dominance += anew[word][2]
freq += 1
end
end
if valence != 0
[valence/freq, arousal/freq, dominance/freq]
else
["Insufficient data for meaningful answer"] * 3
end
end
def default_options
{
:content => "$.message.text[*]",
:expected_receive_period_in_days => 1
}
end
def working?
last_receive_at && last_receive_at > options[:expected_receive_period_in_days].to_i.days.ago
end
def receive(incoming_events)
anew = self.class.sentiment_hash
incoming_events.each do |event|
Utils.values_at(event.payload, options[:content]).each do |content|
sent_values = sentiment_values anew, content
create_event :payload => { :content => content,
:valence => sent_values[0],
:arousal => sent_values[1],
:dominance => sent_values[2],
:original_event => event.payload }
end
end
end
def validate_options
errors.add(:base, "content and expected_receive_period_in_days must be present") unless options[:content].present? && options[:expected_receive_period_in_days].present?
end
def self.sentiment_hash
unless self.anew
self.anew = {}
CSV.foreach Rails.root.join('data/anew.csv') do |row|
self.anew[row[0]] = row.values_at(2, 4, 6).map { |val| val.to_f }
end
end
self.anew
end
def sentiment_values(anew, text)
valence, arousal, dominance, freq = [0] * 4
text.downcase.strip.gsub(/[^a-z ]/, "").split.each do |word|
if anew.has_key? word
valence += anew[word][0]
arousal += anew[word][1]
dominance += anew[word][2]
freq += 1
end
end
if valence != 0
[valence/freq, arousal/freq, dominance/freq]
else
["Insufficient data for meaningful answer"] * 3
end
end
end
end

View file

@ -1,80 +1,78 @@
module Agents
class TranslationAgent < Agent
class TranslationAgent < Agent
cannot_be_scheduled!
cannot_be_scheduled!
description <<-MD
You can use Translation Agent to translate text between natural languages.
Services are provided using Microsoft Translator. You can [sign up](https://datamarket.azure.com/dataset/bing/microsofttranslator) and [register your application](https://datamarket.azure.com/developer/applications/register) to get `client_id` and `client_secret` which are required to use this agent.
`to` must be filled with a [translator language code](http://msdn.microsoft.com/en-us/library/hh456380.aspx).
description <<-MD
You can use Translation Agent to translate text between natural languages.
Services are provided using Microsoft Translator. You can [sign up](https://datamarket.azure.com/dataset/bing/microsofttranslator) and [register your application](https://datamarket.azure.com/developer/applications/register) to get `client_id` and `client_secret` which are required to use this agent.
`to` must be filled with a [translator language code](http://msdn.microsoft.com/en-us/library/hh456380.aspx).
Specify what you would like to translate in `content` field, by specifying key and JSONPath of content to be translated.
Specify what you would like to translate in `content` field, by specifying key and JSONPath of content to be translated.
`expected_receive_period_in_days` is the maximum number of days you would allow to pass between events.
MD
`expected_receive_period_in_days` is the maximum number of days you would allow to pass between events.
MD
event_description <<-MD
User defined
MD
event_description "User defined"
def default_options
{
:client_id => "xxxxxx",
:client_secret => "xxxxxx" ,
:to => "fi",
:expected_receive_period_in_days => 1,
:content => {
:text => "$.message.text",
:content => "$.xyz"
}
}
end
def working?
last_receive_at && last_receive_at > options[:expected_receive_period_in_days].to_i.days.ago
end
def translate(text,to,access_token)
translate_uri = URI 'http://api.microsofttranslator.com/v2/Ajax.svc/Translate'
params = {
:text => text,
:to => to
}
translate_uri.query = URI.encode_www_form params
request = Net::HTTP::Get.new translate_uri.request_uri
request['Authorization'] = "Bearer" + " " + access_token
http = Net::HTTP.new translate_uri.hostname, translate_uri.port
response = http.request request
YAML.load response.body
end
def validate_options
unless options[:client_id].present? && options[:client_secret].present? && options[:to].present? && options[:content].present? && options[:expected_receive_period_in_days].present?
errors.add :base, "client_id,client_secret,to,expected_receive_period_in_days and content are all required"
end
end
def postform(uri,params)
req = Net::HTTP::Post.new(uri.request_uri)
req.form_data = params
Net::HTTP.start(uri.hostname, uri.port, :use_ssl => true) { |http| http.request(req) }
end
def receive(incoming_events)
auth_uri = URI "https://datamarket.accesscontrol.windows.net/v2/OAuth2-13"
response = postform auth_uri, :client_id => options[:client_id],
:client_secret => options[:client_secret],
:scope => "http://api.microsofttranslator.com",
:grant_type =>"client_credentials"
access_token = JSON.parse(response.body)["access_token"]
incoming_events.each do |event|
translated_event = {}
options[:content].each_pair do |key,value|
to_be_translated = Utils.values_at event.payload, value
translated_event[key] = translate to_be_translated.first, options[:to], access_token
end
create_event :payload => translated_event
end
end
def default_options
{
:client_id => "xxxxxx",
:client_secret => "xxxxxx",
:to => "fi",
:expected_receive_period_in_days => 1,
:content => {
:text => "$.message.text",
:content => "$.xyz"
}
}
end
def working?
last_receive_at && last_receive_at > options[:expected_receive_period_in_days].to_i.days.ago
end
def translate(text, to, access_token)
translate_uri = URI 'http://api.microsofttranslator.com/v2/Ajax.svc/Translate'
params = {
:text => text,
:to => to
}
translate_uri.query = URI.encode_www_form params
request = Net::HTTP::Get.new translate_uri.request_uri
request['Authorization'] = "Bearer" + " " + access_token
http = Net::HTTP.new translate_uri.hostname, translate_uri.port
response = http.request request
YAML.load response.body
end
def validate_options
unless options[:client_id].present? && options[:client_secret].present? && options[:to].present? && options[:content].present? && options[:expected_receive_period_in_days].present?
errors.add :base, "client_id,client_secret,to,expected_receive_period_in_days and content are all required"
end
end
def postform(uri, params)
req = Net::HTTP::Post.new(uri.request_uri)
req.form_data = params
Net::HTTP.start(uri.hostname, uri.port, :use_ssl => true) { |http| http.request(req) }
end
def receive(incoming_events)
auth_uri = URI "https://datamarket.accesscontrol.windows.net/v2/OAuth2-13"
response = postform auth_uri, :client_id => options[:client_id],
:client_secret => options[:client_secret],
:scope => "http://api.microsofttranslator.com",
:grant_type => "client_credentials"
access_token = JSON.parse(response.body)["access_token"]
incoming_events.each do |event|
translated_event = {}
options[:content].each_pair do |key, value|
to_be_translated = Utils.values_at event.payload, value
translated_event[key] = translate to_be_translated.first, options[:to], access_token
end
create_event :payload => translated_event
end
end
end
end

View file

@ -19,7 +19,7 @@ module Agents
event_description <<-MD
Events look like this:
{ :message => "Your message" }
{ "message": "Your message" }
MD
def validate_options

View file

@ -19,30 +19,33 @@ module Agents
When in `counts` mode, TwitterStreamAgent events look like:
{
:filter => "hello world",
:count => 25,
:time => 3456785456
"filter": "hello world",
"count": 25,
"time": 3456785456
}
When in `events` mode, TwitterStreamAgent events look like:
{ :filter=>"selectorgadget",
{
"filter": "selectorgadget",
... every Tweet field, including ...
:text=> "something",
:user=>
{ :name=>"Mr. Someone",
:screen_name=>"Someone",
:location=>"Vancouver BC Canada",
:description=> "...",
:followers_count=>486,
:friends_count=>1983,
:created_at=>"Mon Aug 29 23:38:14 +0000 2011",
:time_zone=>"Pacific Time (US & Canada)",
:statuses_count=>3807,
:lang=>"en" },
:retweet_count=>0,
:entities=> ...
:lang=>"en" }
"text": "something",
"user": {
"name": "Mr. Someone",
"screen_name": "Someone",
"location": "Vancouver BC Canada",
"description": "...",
"followers_count": 486,
"friends_count": 1983,
"created_at": "Mon Aug 29 23:38:14 +0000 2011",
"time_zone": "Pacific Time (US & Canada)",
"statuses_count": 3807,
"lang": "en"
},
"retweet_count": 0,
"entities": ...
"lang": "en"
}
MD
default_schedule "11pm"

View file

@ -15,95 +15,25 @@ module Agents
event_description <<-MD
Events are the raw JSON provided by the Twitter API. Should look something like:
{
:created_at=>"Thu Apr 04 13:27:48 +0000 2013",
:id=>319803490421596160,
:id_str=>"319803490421596160",
:text=>
"In which @jeresig goes to an art gallery and is \"the JavaScript programmer\". http://t.co/gt3PT1d3G1",
:source=>
"<a href=\"http://itunes.apple.com/us/app/twitter/id409789998?mt=12\" rel=\"nofollow\">Twitter for Mac</a>",
:truncated=>false,
:in_reply_to_status_id=>nil,
:in_reply_to_status_id_str=>nil,
:in_reply_to_user_id=>nil,
:in_reply_to_user_id_str=>nil,
:in_reply_to_screen_name=>nil,
:user=>
{:id=>2341001,
:id_str=>"2341001",
:name=>"Albert Sun",
:screen_name=>"albertsun",
:location=>"New York, NY",
:description=>
"News apps developer at NYT, formerly WSJ. graduated Penn 2010, geek, journalist, data-viz, nlp, gis, digital economics =)",
:url=>"http://albertsun.info",
:entities=>
{:url=>
{:urls=>
[{:url=>"http://albertsun.info",
:expanded_url=>nil,
:indices=>[0, 21]}]},
:description=>{:urls=>[]}},
:protected=>false,
:followers_count=>1857,
:friends_count=>798,
:listed_count=>115,
:created_at=>"Mon Mar 26 19:22:05 +0000 2007",
:favourites_count=>9,
:utc_offset=>-18000,
:time_zone=>"Eastern Time (US & Canada)",
:geo_enabled=>false,
:verified=>false,
:statuses_count=>2572,
:lang=>"en",
:contributors_enabled=>false,
:is_translator=>false,
:profile_background_color=>"1B2A2B",
:profile_background_image_url=>
"http://a0.twimg.com/profile_background_images/2802438/twitterbg.jpg",
:profile_background_image_url_https=>
"https://si0.twimg.com/profile_background_images/2802438/twitterbg.jpg",
:profile_background_tile=>false,
:profile_image_url=>
"http://a0.twimg.com/profile_images/110500205/profile-square_normal.jpg",
:profile_image_url_https=>
"https://si0.twimg.com/profile_images/110500205/profile-square_normal.jpg",
:profile_link_color=>"0000FF",
:profile_sidebar_border_color=>"87BC44",
:profile_sidebar_fill_color=>"E0FF92",
:profile_text_color=>"000000",
:profile_use_background_image=>true,
:default_profile=>false,
:default_profile_image=>false,
:following=>false,
:follow_request_sent=>false,
:notifications=>false},
:geo=>nil,
:coordinates=>nil,
:place=>nil,
:contributors=>nil,
:retweet_count=>0,
:favorite_count=>0,
:entities=>
{:hashtags=>[],
:urls=>
[{:url=>"http://t.co/gt3PT1d3G1",
:expanded_url=>
"http://www.nytimes.com/2013/04/04/fashion/art-and-techology-a-clash-of-cultures.html?pagewanted=all",
:display_url=>"nytimes.com/2013/04/04/fas",
:indices=>[77, 99]}],
:user_mentions=>
[{:screen_name=>"jeresig",
:name=>"John Resig",
:id=>752673,
:id_str=>"752673",
:indices=>[9, 17]}]},
:favorited=>false,
:retweeted=>false,
:possibly_sensitive=>false,
:lang=>"en"
}
{
... every Tweet field, including ...
"text": "something",
"user": {
"name": "Mr. Someone",
"screen_name": "Someone",
"location": "Vancouver BC Canada",
"description": "...",
"followers_count": 486,
"friends_count": 1983,
"created_at": "Mon Aug 29 23:38:14 +0000 2011",
"time_zone": "Pacific Time (US & Canada)",
"statuses_count": 3807,
"lang": "en"
},
"retweet_count": 0,
"entities": ...
"lang": "en"
}
MD
default_schedule "every_1h"

View file

@ -17,15 +17,15 @@ module Agents
Assuming you're using the iOS application, events look like this:
{
:latitude => "37.12345",
:longitude => "-122.12345",
:timestamp => "123456789.0",
:altitude => "22.0",
:horizontal_accuracy => "5.0",
:vertical_accuracy => "3.0",
:speed => "0.52595",
:course => "72.0703",
:device_token => "..."
"latitude": "37.12345",
"longitude": "-122.12345",
"timestamp": "123456789.0",
"altitude": "22.0",
"horizontal_accuracy": "5.0",
"vertical_accuracy": "3.0",
"speed": "0.52595",
"course": "72.0703",
"device_token": "..."
}
MD

View file

@ -14,26 +14,24 @@ module Agents
Events look like this:
{
:zipcode => 12345,
:date => { :epoch=>"1357959600", :pretty=>"10:00 PM EST on January 11, 2013" },
:high => { :fahrenheit=>"64", :celsius=>"18" },
:low => { :fahrenheit=>"52", :celsius=>"11" },
:conditions => "Rain Showers",
:icon=>"rain",
:icon_url => "http://icons-ak.wxug.com/i/c/k/rain.gif",
:skyicon => "mostlycloudy",
:pop => 80,
:qpf_allday => { :in=>0.24, :mm=>6.1 },
:qpf_day => { :in=>0.13, :mm=>3.3 },
:qpf_night => { :in=>0.03, :mm=>0.8 },
:snow_allday => { :in=>0, :cm=>0 },
:snow_day => { :in=>0, :cm=>0 },
:snow_night => { :in=>0, :cm=>0 },
:maxwind => { :mph=>15, :kph=>24, :dir=>"SSE", :degrees=>160 },
:avewind => { :mph=>9, :kph=>14, :dir=>"SSW", :degrees=>194 },
:avehumidity => 85,
:maxhumidity => 93,
:minhumidity => 63
"zipcode": 12345,
"date": {
"epoch": "1357959600",
"pretty": "10:00 PM EST on January 11, 2013"
},
"high": {
"fahrenheit": "64",
"celsius": "18"
},
"low": {
"fahrenheit": "52",
"celsius": "11"
},
"conditions": "Rain Showers",
"icon": "rain",
"icon_url": "http://icons-ak.wxug.com/i/c/k/rain.gif",
"skyicon": "mostlycloudy",
...
}
MD

View file

@ -36,11 +36,7 @@ module Agents
MD
event_description do
<<-MD
Events will have the fields you specified. Your options look like:
#{PP.pp(options[:extract], "")}
MD
"Events will have the fields you specified. Your options look like:\n\n #{Utils.pretty_print options[:extract]}"
end
default_schedule "every_12h"

View file

@ -16,51 +16,51 @@ module Agents
MD
event_description <<-MD
Events are the raw JSON provided by the Twitter API. Should look something like:
Events are the raw JSON provided by the Weibo API. Should look something like:
{
"created_at": "Tue May 31 17:46:55 +0800 2011",
"id": 11488058246,
"text": "求关注。",
"source": "<a href=\"http://weibo.com\" rel=\"nofollow\">新浪微博</a>",
"favorited": false,
"truncated": false,
"in_reply_to_status_id": "",
"in_reply_to_user_id": "",
"in_reply_to_screen_name": "",
"geo": null,
"mid": "5612814510546515491",
"reposts_count": 8,
"comments_count": 9,
"annotations": [],
"user": {
"id": 1404376560,
"screen_name": "zaku",
"name": "zaku",
"province": "11",
"city": "5",
"location": "北京 朝阳区",
"description": "人生五十年,乃如梦如幻;有生斯有死,壮士复何憾。",
"url": "http://blog.sina.com.cn/zaku",
"profile_image_url": "http://tp1.sinaimg.cn/1404376560/50/0/1",
"domain": "zaku",
"gender": "m",
"followers_count": 1204,
"friends_count": 447,
"statuses_count": 2908,
"favourites_count": 0,
"created_at": "Fri Aug 28 00:00:00 +0800 2009",
"following": false,
"allow_all_act_msg": false,
"remark": "",
"geo_enabled": true,
"verified": false,
"allow_all_comment": true,
"avatar_large": "http://tp1.sinaimg.cn/1404376560/180/0/1",
"verified_reason": "",
"follow_me": false,
"online_status": 0,
"bi_followers_count": 215
{
"created_at": "Tue May 31 17:46:55 +0800 2011",
"id": 11488058246,
"text": "求关注。",
"source": "<a href=\"http://weibo.com\" rel=\"nofollow\">新浪微博</a>",
"favorited": false,
"truncated": false,
"in_reply_to_status_id": "",
"in_reply_to_user_id": "",
"in_reply_to_screen_name": "",
"geo": null,
"mid": "5612814510546515491",
"reposts_count": 8,
"comments_count": 9,
"annotations": [],
"user": {
"id": 1404376560,
"screen_name": "zaku",
"name": "zaku",
"province": "11",
"city": "5",
"location": "北京 朝阳区",
"description": "人生五十年,乃如梦如幻;有生斯有死,壮士复何憾。",
"url": "http://blog.sina.com.cn/zaku",
"profile_image_url": "http://tp1.sinaimg.cn/1404376560/50/0/1",
"domain": "zaku",
"gender": "m",
"followers_count": 1204,
"friends_count": 447,
"statuses_count": 2908,
"favourites_count": 0,
"created_at": "Fri Aug 28 00:00:00 +0800 2009",
"following": false,
"allow_all_act_msg": false,
"remark": "",
"geo_enabled": true,
"verified": false,
"allow_all_comment": true,
"avatar_large": "http://tp1.sinaimg.cn/1404376560/180/0/1",
"verified_reason": "",
"follow_me": false,
"online_status": 0,
"bi_followers_count": 215
}
}
MD

View file

@ -22,10 +22,9 @@
<% if @agent.new_record? %>
<div class="control-group type-select">
<%= image_tag "spinner-arrows.gif", :class => "spinner" %>
<%= f.label :type, :class => 'control-label' %>
<div class="controls">
<%= f.select :type, options_for_select(Agent.types.map {|type| [type.to_s.gsub(/^.*::/, ''), type.to_s] }, @agent.type), :class => 'span4' %>
<%= f.select :type, options_for_select(Agent.types.map {|type| [type.to_s.gsub(/^.*::/, ''), type.to_s] }, @agent.type), {}, :class => 'span4 select2' %>
</div>
</div>
<% end %>
@ -40,7 +39,7 @@
<div class="control-group">
<%= f.label :schedule, :class => 'control-label' %>
<div class="controls schedule-region" data-can-be-scheduled="<%= @agent.can_be_scheduled? %>">
<%= f.select :schedule, options_for_select(Agent::SCHEDULES.map {|s| [s.humanize.titleize, s] }, @agent.schedule), :class => 'span4' %>
<%= f.select :schedule, options_for_select(Agent::SCHEDULES.map {|s| [s.humanize.titleize, s] }, @agent.schedule), {}, :class => 'span4' %>
<span class='cannot-be-scheduled text-info'>This type of Agent cannot be scheduled.</span>
</div>
</div>
@ -51,7 +50,7 @@
<%= f.select(:source_ids,
options_for_select((current_user.agents - [@agent]).map {|s| [s.name, s.id] },
@agent.source_ids),
{}, { :multiple => true, :size => 5, :class => 'span4 multi-select' }) %>
{}, { :multiple => true, :size => 5, :class => 'span4 select2' }) %>
<span class='cannot-receive-events text-info'>This type of Agent cannot receive events.</span>
</div>
</div>

View file

@ -2,7 +2,10 @@
<div class='row'>
<div class='span12'>
<div class="page-header">
<h2>Editing your <%= @agent.short_type %></h2>
<h2>
Editing your <%= @agent.short_type %>
<%= image_tag "spinner-arrows.gif", :class => "spinner" %>
</h2>
</div>
<%= render 'form' %>

View file

@ -2,7 +2,10 @@
<div class='row'>
<div class='span12'>
<div class="page-header">
<h2>Create a new Agent</h2>
<h2>
Create a new Agent
<%= image_tag "spinner-arrows.gif", :class => "spinner" %>
</h2>
</div>
<%= render 'form' %>

View file

@ -90,12 +90,12 @@
<p>
<b>Options:</b>
<pre><%= PP.pp(@agent.options, "") %></pre>
<pre><%= JSON.pretty_generate @agent.options %></pre>
</p>
<p>
<b>Memory:</b>
<pre><%= PP.pp(@agent.memory, "") %></pre>
<pre><%= JSON.pretty_generate @agent.memory %></pre>
</p>
</div>
</div>

View file

@ -7,7 +7,7 @@
<p>
<b>Payload:</b>
<pre><%= PP.pp(@event.payload, "") %></pre>
<pre><%= JSON.pretty_generate @event.payload %></pre>
</p>
<% if @event.lat && @event.lng %>

View file

@ -2,9 +2,23 @@ require 'jsonpath'
require 'cgi'
module Utils
# Unindents if the indentation is 2 or more characters.
def self.unindent(s)
s.gsub(/^#{s.scan(/^\s+/).select {|i| i.length > 1 }.min_by{|l|l.length}}/, "")
s = s.gsub(/\t/, ' ').chomp
min = ((s.split("\n").find {|l| l !~ /^\s*$/ })[/^\s+/, 0] || "").length
if min > 0
s.gsub(/^#{" " * min}/, "")
else
s
end
end
def self.pretty_print(struct, indent = true)
output = JSON.pretty_generate(struct)
if indent
output.gsub(/\n/i, "\n ").tap { |a| p a }
else
output
end
end
def self.recursively_symbolize_keys(object)

View file

@ -1,6 +1,32 @@
require 'spec_helper'
describe Utils do
describe "#unindent" do
it "unindents to the level of the greatest consistant indention" do
Utils.unindent(<<-MD).should == "Hello World"
Hello World
MD
Utils.unindent(<<-MD).should == "Hello World\nThis is\nnot indented"
Hello World
This is
not indented
MD
Utils.unindent(<<-MD).should == "Hello World\n This is\n indented\nthough"
Hello World
This is
indented
though
MD
Utils.unindent("Hello\n I am indented").should == "Hello\n I am indented"
a = " Events will have the fields you specified. Your options look like:\n\n {\n \"url\": {\n \"css\": \"#comic img\",\n \"attr\": \"src\"\n },\n \"title\": {\n \"css\": \"#comic img\",\n \"attr\": \"title\"\n }\n }\"\n"
Utils.unindent(a).should == "Events will have the fields you specified. Your options look like:\n\n {\n \"url\": {\n\"css\": \"#comic img\",\n\"attr\": \"src\"\n },\n \"title\": {\n\"css\": \"#comic img\",\n\"attr\": \"title\"\n }\n }\""
end
end
describe "#value_at" do
it "returns the value at a JSON path" do
Utils.value_at({ :foo => { :bar => :baz }}.to_json, "foo.bar").should == "baz"

View file

@ -212,7 +212,7 @@ JSONEditor.prototype.showFunctionButtons = function(insider) {
}).text('Redo')).append($('<a id="toggle_view" href="#" style="padding-right: 10px;"></a>').click(function() {
self.toggleBuilder();
return false;
}).text('Toggle View').css("float", "right"));
}).text('Toggle View'));
this.container.prepend(this.functionButtons);
this.container.height(this.container.height() + this.functionButtons.height() + 5);
}