Skip to content

Instantly share code, notes, and snippets.

@akasper
Created December 22, 2010 20:11
Show Gist options
  • Save akasper/752029 to your computer and use it in GitHub Desktop.
Save akasper/752029 to your computer and use it in GitHub Desktop.
I CAN HAZ LIKES COUNT
class Moderation::GraphObject
include Mongoid::Document
include Mongoid::Timestamps
before_save :flag_fb_comments_and_likes_counts_changes
after_save :update_fb_comments_and_likes_counts_on_dispatch!
include Moderation::Remover
include Moderation::Flagger
include Moderation::Stateful
include ActionView::Helpers::DateHelper
self.collection_name = 'fb_graph_objects'
field :fb_id
field :from, :type => Hash
field :page_id
field :post_id
field :post_link
field :created_time, :type => Time
field :updated_time, :type => Time
field :comments_count, :type => Integer
field :flags, :type => Integer
field :likes_count, :type => Integer
field :fb_comments_count, :type => Integer
validates_presence_of :fb_id, :page_id, :comments_count, :flags, :likes_count
scope :from, (lambda do |from_id|
where(:"from.id" => from_id)
end)
scope :not_from, (lambda do |from_id|
excludes(:"from.id" => from_id)
end)
def flag_fb_comments_and_likes_counts_changes
@fb_comments_and_likes_counts_changed = post_id ? (self.fb_comments_count_changed? || self.likes_count_changed?) : false
end
def update_fb_comments_and_likes_counts_on_dispatch!
if post_id && @fb_comments_and_likes_counts_changed
Dispatch.update_comments_and_likes_counts!(self.post_id, self.fb_comments_count, self.likes_count)
end
end
def stream
FacebookPageStream.active.find_by_stream_identifier(page_id)
end
# Override this in each subclass to provide accurate behaviour
def permalink
self.post_link
end
def increment_flags
update_attributes(:flags => flags + 1)
end
def decrement_flags
update_attributes(:flags => flags - 1)
end
def update_from_graph_data!(graph_data)
db_updated = updated_time
fb_updated = Time.parse(graph_data['updated_time']) rescue nil
if (db_updated && fb_updated && db_updated < fb_updated) || (!db_updated && fb_updated)
graph_data['updated_time'] = fb_updated
graph_data.delete('id')
graph_data.delete('created_time')
# preserve comments if necessary
comments = Moderation::GraphObject.should_preserve_comments?(graph_data, page_id, self.class) ? graph_data['comments']['data'] : []
graph_data.delete_recursively(%w[comments likes])
merged_graph_data = self.raw_attributes.merge(graph_data)
self.update_attributes!(merged_graph_data)
comments.each do |comment|
Moderation::Comment.for_graph_data!(comment, page_id, fb_id)
end
# refetch to reflect embedded document changes
Moderation::GraphObject.where(:fb_id => fb_id).first
end
end
def self.find_post_target(post_data)
post_id = post_data['id']
regex = Regexp.new("^[0-9]+_")
if (post_id =~ regex) != 0
raise ArgumentError.new("No parent id prefix was present on the supplied post_data['id']: #{post_id}")
end
target = Moderation::GraphObject.criteria.id(post_id.sub(regex, '')).first || Moderation::GraphObject.criteria.id(post_id).first
# Handle the special case of photo album posts, in which case the ids don't match up and we try to match by name.
target ||= Moderation::Album.where(:name => post_data['name']).first if post_data['type'] == 'photo' && post_data['name']
target
end
# Returns true if the supplied graph data is a wall post "from" the page "to"
# the page's wall itself.
def self.page_post?(graph_data, k, page_id)
from_id = graph_data['from']['id'] rescue nil
(k == Moderation::WallPost) && (from_id == page_id)
end
def self.for_graph_data!(graph_data, k, page_id, parent_id)
if existing = k.criteria.id(graph_data['id']).first
logger.debug "Found existing #{k} with fb_id #{graph_data['id']}"
existing.update_from_graph_data!(graph_data)
else
self.create_from_graph_data!(graph_data, page_id, parent_id, k)
end
end
def self.for_post_data!(post_data)
if existing = Moderation::GraphObject.find(:conditions => {:post_id => post_data['id']}).first
Rails.logger.debug "Post id #{post_data['id']} already set for target #{existing.fb_id} - updating existing"
elsif existing = self.find_post_target(post_data)
Rails.logger.debug "Found target #{existing.fb_id} - updating post_id to #{post_data['id']}}"
existing.post_id = post_data['id']
existing.post_link = link_from_post_data(post_data)
end
if existing
existing.fb_comments_count = post_data['comments']['count'] rescue 0
existing.likes_count = (post_data['likes']['count'] rescue post_data['likes']) || 0
existing.save!
end
existing
end
def marked_up_message
graph_message
end
def to_jqgrid
{
:id => self.fb_id,
:friendly_time => friendly_time,
:marked_up_message => graph_message,
:comments_count => comments_count,
:likes_count => likes_count,
:flags_count => flags,
:object_type => display_type
}
end
def friendly_time
time_ago_in_words(self.updated_time) || "?"
rescue
"?"
end
def try_time
self.updated_time || self.created_time
rescue
"?"
end
def try_message
message
rescue
"[No Content]"
end
def friendly_date offset=0
(updated_time.to_date - (offset.to_i * 60).seconds).strftime("%B %d at %I:%M%p").
gsub("AM", "am").gsub("PM", "pm")
rescue
"?"
end
def graph_message
mark_up_text
end
def display_type
self.class.name.gsub(/Moderation\:\:/, '')
end
def increment_comments_count
update_attributes(:comments_count => comments_count+1)
end
def decrement_comments_count
update_attributes(:comments_count => comments_count-1) if comments_count > 0
end
def creator_profile_url
"http://www.facebook.com/profile.php?id=#{from["id"]}" unless from["id"].blank?
end
def self.generate_csv posts
cols = ['Facebook Post ID', 'Permalink', 'Created At', 'Creator Profile', 'Message', "Type"]
csv_string = FasterCSV.generate do |csv|
csv << cols
posts.each do |p|
csv << [p.fb_id, p.permalink, p.try_time, p.creator_profile_url, p.try_message, p.display_type]
end
end
end
def partial_path
"moderation/" + self.class.name.demodulize.underscore
end
private
def self.create_from_graph_data!(graph_data, page_id, parent_id, k)
comments = should_preserve_comments?(graph_data, page_id, k) ? graph_data['comments']['data'] : []
graph_data = prepare(graph_data, page_id, parent_id, k)
obj = k.create!(graph_data)
if comments.any?
comments.each do |comment|
Moderation::Comment.for_graph_data!(comment, page_id, obj.fb_id)
end
# if we need to preseve comments, save them
obj = k.where(:fb_id => obj.fb_id).first
end
obj
end
def self.prepare(graph_data, page_id, parent_id, k)
doc = {
'fb_id' => graph_data['id'],
'flags' => 0,
'comments_count' => 0,
'fb_comments_count' => 0,
'likes_count' => 0,
'page_id' => page_id,
'state' => 'active'
}
doc.merge!(graph_data)
# note that links have only created_time, statuses have only updated_time
# we want to ensure both are set, so we cross-pollinate them as defaults
created = Time.parse(graph_data['created_time']) rescue nil
updated = Time.parse(graph_data['updated_time']) rescue nil
doc['created_time'] = created || updated || Time.at(0)
doc['updated_time'] = updated || created || Time.at(0)
if [Moderation::EventWallPost, Moderation::Music, Moderation::Swf, Moderation::WallPost].include? k
fb_comments_count = graph_data['comments']['count'] rescue 0
likes_count = graph_data['likes']['count'] rescue graph_data['likes'] || 0
end
doc.merge!( {
'post_id' => graph_data['id'],
'fb_comments_count' => fb_comments_count,
'likes_count' => likes_count} )
doc.delete_recursively(%w[comments likes])
end
def self.link_from_post_data(post_data)
# Use the actions link if present on the post, otherwise generate the post link as best we know how.
post_data['actions'][0]['link'] rescue link_from_post_id(post_data['id'])
end
def self.link_from_post_id(post_id)
ids = post_id.split('_')
"http://www.facebook.com/#{ids[0]}/posts/#{ids[1]}"
end
# The purpose of this method is to determine whether a particular
# piece of graph data is subject to the Facebook bug described here:
# http://bugs.developers.facebook.net/show_bug.cgi?id=11156
# This occurs when a photo is posted to the wall by a user other
# than the page.
def self.should_preserve_comments?(graph_data, page_id, k)
comments = graph_data['comments']['data'] rescue nil
comments && (k == Moderation::WallPost) && graph_data['type'] == 'photo' && (graph_data['from']['id'] != page_id)
end
# The purpose of this method is to determine whether a particular
# piece of graph data is subject to the Facebook bug described here:
# http://bugs.developers.facebook.net/show_bug.cgi?id=11156
# This occurs when a photo is posted to the wall by a user other
# than the page.
def self.should_preserve_comments?(graph_data, page_id, k)
comments = graph_data['comments']['data'] rescue nil
comments && (k == Moderation::WallPost) && graph_data['type'] == 'photo' && (graph_data['from']['id'] != page_id)
end
def self.logger
RAILS_DEFAULT_LOGGER
end
end
@akasper
Copy link
Author

akasper commented Dec 22, 2010

Mongoid::Errors::Validations in 'Moderation::GraphObject#to_jqgrid should transform the object into a hash for jqGrid consumption'
Validation Failed: Likes count can't be blank
/Users/akasper/Projects/Vitrue/publisher/vendor/ruby/1.8/bundler/gems/mongoid-2bd2582f2264/lib/mongoid/persistence.rb:218:in fail_validate!' /Users/akasper/Projects/Vitrue/publisher/vendor/ruby/1.8/bundler/gems/mongoid-2bd2582f2264/lib/mongoid/persistence.rb:178:increate!'
/Users/akasper/Projects/Vitrue/publisher/app/models/moderation/graph_object.rb:220:in create_from_graph_data!' /Users/akasper/Projects/Vitrue/publisher/app/models/moderation/graph_object.rb:118:infor_graph_data!'
/Users/akasper/Projects/Vitrue/publisher/spec/models/moderation/graph_object_spec.rb:55:
/Users/akasper/Projects/Vitrue/publisher/spec/models/moderation/graph_object_spec.rb:47:in each' /Users/akasper/Projects/Vitrue/publisher/spec/models/moderation/graph_object_spec.rb:47: /Users/akasper/Projects/Vitrue/publisher/vendor/ruby/1.8/gems/spork-0.8.4/bin/../lib/spork/runner.rb:75:inrun'
/Users/akasper/Projects/Vitrue/publisher/vendor/ruby/1.8/gems/spork-0.8.4/bin/../lib/spork/runner.rb:9:in `run'

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment