Permalink
Browse files

Complete overhaul of app.rb and the parser libraries. Everything is m…

…ore standardized with a `gather` method for each parser, a hash to contain all the parser instances, and more obvious weighting algorithms.
  • Loading branch information...
1 parent 39cdd2e commit 20d6ffd62203142b6d0bd082800755d892866f17 Jake McGinty committed Mar 12, 2012
View
186 app.rb
@@ -4,114 +4,100 @@
require 'bundler/setup'
require 'purdy-print'
require 'twitter-sentiment'
-include PurdyPrint
+include PurdyPrint # colorful stylized console log library
+
+stdout_mutex = Mutex.new
class TwitterBeats
- @@debug = :high # PurdyPrint debug var
-
- @@parsers = {
- :text_mood => {
- :instance => nil,
- :result => nil,
- :weight => nil,
- },
- :text_mood => {
- :instance => nil,
- :result => nil,
- :weight => nil,
- },
- }
+ @@debug = :med # PurdyPrint debug var
+ @@score_bounds = [-10,10]
+ attr_reader :parsers
+
+ def limit_score score
+ return 0 if score.nil?
+ score = score > @@score_bounds[1] ? @@score_bounds[1] : score
+ score = score < @@score_bounds[0] ? @@score_bounds[0] : score
+ return score.round
+ end
+
+ def happiness
+
+ return limit_score(@parsers[:text_mood][:result][:score]*0.7+@parsers[:user_image][:result][:score]*0.2+@parsers[:user_stats][:result][:description_score]*0.1) \
+ unless @parsers[:text_mood][:result][:score].nil? \
+ or @parsers[:user_image][:result][:score].nil? \
+ or @parsers[:user_stats][:result][:description_score].nil?
+
+ return limit_score(@parsers[:text_mood][:result][:score]*0.8+@parsers[:user_image][:result][:score]*0.2) \
+ unless @parsers[:text_mood][:result][:score].nil? \
+ or @parsers[:user_image][:result][:score].nil?
+
+ return limit_score(@parsers[:text_mood][:result][:score]*0.9+@parsers[:user_stats][:result][:description_score]*0.1) \
+ unless @parsers[:text_mood][:result][:score].nil? \
+ or @parsers[:user_stats][:result][:description_score].nil?
+
+ return limit_score(@parsers[:text_mood][:result][:score]) \
+ unless @parsers[:text_mood][:result][:score].nil?
+
+ return 0
+ end
+
+ def paint_score num
+ return Paint["nil", :italic, :yellow] if num.nil?
+ return Paint[num.to_s, :bold, :red] if num < 0
+ return Paint[num.to_s, :bold, :green] if num > 0
+ return Paint[num.to_s, :bold]
+ end
def initialize
- pp :info, "TwitterBeats initializing..."
- textmood = TwitterSentiment::Parser::TextMood.new :afinn
- userinfo = TwitterSentiment::Parser::UserInfo.new
- output_send = TwitterSentiment::Output::Send.new
- random = TwitterSentiment::Parser::Randomness.new
+ pp :category, Paint["Welcome to ", :bold] + Paint["Twitter",:bright,:red] + Paint["Beats",:bright,:blue]
+ # Since UserStats and TextMood can both use the same TextMood instance, we can send it the same
+ # one and avoid double the generation.
+ textmood_global = TwitterSentiment::Parser::TextMood.new(:afinn_emo)
+ @parsers = {
+ :text_mood => { :instance => textmood_global },
+ :user_image => { :instance => TwitterSentiment::Parser::FaceRecon.new },
+ :user_stats => { :instance => textmood_global },
+ :randomness => { :instance => TwitterSentiment::Parser::Randomness.new },
+ }
+
+ out = TwitterSentiment::Output::Send.new
+
TwitterSentiment::Input::Twitter.new({
:status_callback => lambda { |status|
- weight, mood = {}, {}
- pp :seperator
- # text weight
- text_score = textmood.score(status.text)
- weight[:text] = text_score[:score]
- mood [:text] = mood_from_score weight[:text]
- pp mood[:text], "text score: #{weight[:text].to_s.ljust(7)}tweet: #{status.text}", :med
-
- #user stalking
- info = userinfo.gather(status.user)
- pp :info, "Boring images: #{info[0]}"
- pp :info, "Followers per tweet: #{info[1]}"
- weight[:description] = info[3][:score]
- weight[:img] = info[2]
- mood[:description] = mood_from_score weight[:description]
- mood[:img] = mood_from_score weight[:img]
- pp mood[:description], "Desc. score: #{weight[:description].to_s.ljust(8)}User description: #{status.user.description}"
-
- #symbol checking
- syms = random.symbol_count(status.text)
- pp :info, "syms: #{syms}"
-
- #compile data (not JSON)
- #put this is a separate method?
- total_happiness = Integer(weight[:text]) + Integer(weight[:img])/10 + Integer(weight[:description]) #textmood + imgscore/10 + userdescription
- capped_total_happiness = total_happiness
- capped_total_happiness = 10 if total_happiness > 10
- capped_total_happiness = -10 if total_happiness < -10
-
- total_excitement = (Integer(info[1]*10) - Integer(info[0]*5))*2 #followerspertweet * 10 - boringimages * 5
- capped_total_excitement = total_excitement
- capped_total_excitement = 10 if total_excitement > 10
- capped_total_excitement = -10 if total_excitement < -10
-
- total_randomness = (-10 + Integer(syms[1]) + rand(5))*2 #-10 + '?' + rand(5)
- capped_total_randomness = total_randomness
- capped_total_randomness = 10 if total_excitement > 10
- capped_total_randomness = -10 if total_excitement < -10
- data = {
- "input" => {
- "source" => "twitter",
- "username" => status.user.screen_name,
- "displayname" => status.user.name,
- "userid" => status.user.id_str,
- "url" => "https://twitter.com/#!/" + status.user.screen_name + "/status/" + status.id_str + "/",
- "userimgurl" => status.user.profile_image_url.gsub(/_normal/, ''),
- "raw_input" => status.text,
- "text" => text_score[:stripped_text],
- "metadata" => nil #fix this
- }, #input
- "weights" => {
- "happiness" => capped_total_happiness, #filler algorithm
- "excitement" => capped_total_excitement, #filler algorithm
- "randomness" => capped_total_randomness #literally random
- }, #weights
- "sentiment" => {
- "text" => {
- "total_score" => total_happiness, #stolen from "happiness"
- "positive_score" => nil, #fix this
- "negative_score" => nil #fix this
- },
- "tweet" => {
- "hash_obnoxiousess" => status.entities.hashtags.length, #fix this
- "retweet" => status.retweeted
- },
- "face" => {
- "smiling" => nil,
- "confidence" => nil,
- }
- } #sentiment
- } #data
- pp :info, "#{data}", :high
- output_send.send_gen data
- },
+ stdout = []
+ stdout << fmt(:separator) # separate initialization text from tweet fun
+ # text weight
+ stdout << fmt(:info, "#{Paint["TWEET - ",:yellow]}#{status.text}")
+ @parsers.each do |parser, c|
+ c[:result] = c[:instance].gather(status)
+ #TWEET - #
+ stdout << fmt(c[:result][:score], " #{Paint["|",:yellow]} #{paint_score(c[:result][:score].round(2))} #{Paint["<-",[50,50,50]]} #{parser.to_s}", :med)
+ end
+
+ weights = {
+ # happiness = 70% tweet, 20% image, 10% description
+ :happiness => happiness,
+ # excitement = follows per tweet
+ # TODO: make this actually excitement of post
+ :excitement => limit_score(@parsers[:user_stats][:result][:follows_per_tweet]),
+ # confusion = number of question marks and a hint of randomness
+ :randomness => limit_score(@parsers[:randomness][:result][:questions] + rand(5) * 2),
+ }
+
+ stdout << fmt(weights[:happiness], "h/e/c = #{weights[:happiness]}/#{weights[:excitement]}/#{weights[:randomness]}", :med)
+ puts stdout.join("\n") # mutex-free debug outputs
+ out.send_gen weights, status, parsers
+ },
})
end
end
-begin
- # rile the beast
- TwitterBeats.new
-rescue SystemExit, Interrupt
- pp :warn, "Interrupt received, quitting..."
- exit
+if __FILE__ == $0
+ begin
+ # rile the beast
+ TwitterBeats.new
+ rescue SystemExit, Interrupt
+ pp :warn, "Interrupt received, quitting..."
+ exit
+ end
end
View
@@ -9,12 +9,17 @@ def level_to_score level
level = 3 if level == :high
return level
end
- def mood_from_score score
- :bhargav if score == 0
- :happy if score > 0
- :sad if score < 0
+ def number_to_mood score
+ return :happy if score > 0
+ return :sad if score < 0
+ return :bhargav
end
- def pp mood=:info, msg="", debug_level=:off
+ def fmt mood=:info, msg="", debug_level=:off
+ case mood
+ when Numeric
+ mood = number_to_mood mood
+ end
+
moods = {
:info => Paint["[info] ", [50,50,50]],
:debug => Paint["[dbug] ", [87,14,88]],
@@ -23,10 +28,15 @@ def pp mood=:info, msg="", debug_level=:off
:happy => Paint["[ :) ] ", [151,192,12]],
:sad => Paint["[ :( ] ", [171,7,97]],
:bhargav => Paint["[ :| ] ", [200,200,200]],
- :seperator => Paint["======" , [50,50,50]]
+ :separator => Paint["======" , [50,50,50]],
+ :category => Paint[" == ", :yellow],
}
mood=:info if not moods.member? mood
- puts moods[mood] + msg if level_to_score(debug_level) < level_to_score(@@debug)
+ return moods[mood] + msg if level_to_score(debug_level) <= level_to_score(@@debug)
+ end
+ def pp mood=:info, msg="", debug_level=:off
+ msg = fmt(mood, msg, debug_level)
+ puts msg unless msg.nil?
end
def pp_exception e
pp :error, "Exception caught (#{e})"
@@ -2,43 +2,50 @@
require 'tweetstream'
require 'purdy-print'
require 'paint'
+require 'fiber'
require 'twitter-sentiment/prefs/defaults'
require 'twitter-sentiment/prefs/secrets'
module TwitterSentiment
- module Input
- include PurdyPrint
- class Twitter
- def initialize options
- default = TwitterSentiment::Prefs::Defaults.twitter
- @client = TweetStream::Client.new(TwitterSentiment::Prefs::Secrets.twitter)
+ module Input
+ include PurdyPrint
+ class Twitter
+ def initialize options
+ default = TwitterSentiment::Prefs::Defaults.twitter
+ @client = TweetStream::Client.new(TwitterSentiment::Prefs::Secrets.twitter)
+ @fibers = [Fiber.current]
+ # Chain of fools
+ @client.on_delete { |status_id, user_id|
+ #TODO: be nice and delete, or be a biznatch?
+ }.on_limit { |skip_count|
+ #TODO: something
+ }.on_reconnect { |timeout, retries|
+ #TODO: anything necessary? doubt it.
+ }
- # Chain of fools
- @client.on_delete { |status_id, user_id|
- #TODO: be nice and delete, or be a biznatch?
- }.on_limit { |skip_count|
- #TODO: something
- }.on_reconnect { |timeout, retries|
- #TODO: anything necessary? doubt it.
- }
-
- # Currently will track all references to default specified username.
- #@client.track("@#{default[:user_name]}") do |status|
- # puts "[#{status.user.screen_name}] #{status.text}"
- #end
- @client.track(default[:search_phrase]) do |status|
- begin
- # raw debug tweet output
- pp :debug, "#{Paint['['+status.user.screen_name+']', :yellow]} #{status.text}", :high
-
- # call the status-received callback
- options[:status_callback].call(status)
- rescue Interrupt
- raise
- rescue Exception => e
- pp_exception e
- end
- end
- end
- end # Twitter
- end # Input
+ # Currently will track all references to default specified username.
+ #@client.track("@#{default[:user_name]}") do |status|
+ # puts "[#{status.user.screen_name}] #{status.text}"
+ #end
+ begin
+ @client.track(default[:search_phrase]) do |status|
+ begin
+ # raw debug tweet output
+ pp :debug, "#{Paint['['+status.user.screen_name+']', :yellow]} #{status.text}", :high
+ @fibers << Thread.new do
+ # call the status-received callback
+ options[:status_callback].call(status)
+ end
+ @fibers.last.run
+ rescue Interrupt
+ raise
+ rescue Exception => e
+ pp_exception e
+ end
+ end
+ rescue EventMachine::ConnectionError => e
+ pp :error, "Couldn't connect to Twitter."
+ end
+ end
+ end # Twitter
+ end # Input
end # twitter-sentiment
Oops, something went wrong.

0 comments on commit 20d6ffd

Please sign in to comment.