Created
December 22, 2010 20:11
-
-
Save akasper/752029 to your computer and use it in GitHub Desktop.
I CAN HAZ LIKES COUNT
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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:in
create!'/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:in
for_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:in
run'/Users/akasper/Projects/Vitrue/publisher/vendor/ruby/1.8/gems/spork-0.8.4/bin/../lib/spork/runner.rb:9:in `run'