profile_suggestion.rb 6.69 KB
class ProfileSuggestion < ActiveRecord::Base
  belongs_to :person
  belongs_to :suggestion, :class_name => 'Profile', :foreign_key => :suggestion_id

  attr_accessible :person, :suggestion, :suggestion_type, :categories, :enabled

  before_create do |profile_suggestion|
    profile_suggestion.suggestion_type = self.suggestion.class.to_s
  end

  acts_as_having_settings :field => :categories

  validate :must_be_a_valid_category, :on => :create
  def must_be_a_valid_category
    if categories.keys.map { |cat| self.respond_to?(cat)}.include?(false)
      errors.add(:categories, 'Category must be valid')
    end
  end

  validates_uniqueness_of :suggestion_id, :scope => [ :person_id ]
  scope :of_person, :conditions => { :suggestion_type => 'Person' }
  scope :of_community, :conditions => { :suggestion_type => 'Community' }
  scope :enabled, :conditions => { :enabled => true }

  # {:category_type => ['category-icon', 'category-label']}
  CATEGORIES = {
    :common_friends => ['menu-people', _('Friends in common')],
    :common_communities => ['menu-community',_('Communities in common')],
    :common_tags => ['edit', _('Tags in common')]
  }

  CATEGORIES.keys.each do |category|
    settings_items category.to_sym
    attr_accessible category.to_sym
  end

  def category_icon(category)
    'icon-' + ProfileSuggestion::CATEGORIES[category][0]
  end

  def category_label(category)
    ProfileSuggestion::CATEGORIES[category][1]
  end

  RULES = %w[
    friends_of_friends
    people_with_common_communities
    people_with_common_tags
    communities_with_common_friends
    communities_with_common_tags
  ]

  # Number of suggestions
  N_SUGGESTIONS = 30

  # Number max of attempts
  MAX_ATTEMPTS = N_SUGGESTIONS * 2

  # Number of friends in common
  COMMON_FRIENDS = 2

  # Number of communities in common
  COMMON_COMMUNITIES = 1

  # Number of friends in common
  COMMON_TAGS = 2

  def self.friends_of_friends(person)
    person_attempts = 0
    person_friends = person.friends
    person_friends.each do |friend|
      friend.friends.includes.each do |friend_of_friend|
        person_attempts += 1
        return unless person.profile_suggestions.count < N_SUGGESTIONS && person_attempts < MAX_ATTEMPTS
        unless friend_of_friend == person || friend_of_friend.is_a_friend?(person) || person.already_request_friendship?(friend_of_friend)
          common_friends = friend_of_friend.friends & person_friends
          if common_friends.size >= COMMON_FRIENDS
            suggestion = person.profile_suggestions.find_or_initialize_by_suggestion_id(friend_of_friend.id)
            suggestion.common_friends = common_friends.size
            suggestion.save
          end
        end
      end
    end
  end

  def self.people_with_common_communities(person)
    person_attempts = 0
    person_communities = person.communities
    person_communities.each do |community|
      community.members.each do |member|
        person_attempts += 1
        return unless person.profile_suggestions.count < N_SUGGESTIONS && person_attempts < MAX_ATTEMPTS
        unless member == person || member.is_a_friend?(person) || person.already_request_friendship?(member)
          common_communities = person_communities & member.communities
          if common_communities.size >= COMMON_COMMUNITIES
            suggestion = person.profile_suggestions.find_or_initialize_by_suggestion_id(member.id)
            suggestion.common_communities = common_communities.size
            suggestion.save
          end
        end
      end
    end
  end

  def self.people_with_common_tags(person)
    person_attempts = 0
    tags = person.article_tags.keys
    tags.each do |tag|
      person_attempts += 1
      return unless person.profile_suggestions.count < N_SUGGESTIONS && person_attempts < MAX_ATTEMPTS
      tagged_content = ActsAsTaggableOn::Tagging.includes(:taggable).where(:tag_id => ActsAsTaggableOn::Tag.find_by_name(tag))
      tagged_content.each do |tg|
        author = tg.taggable.created_by
        unless author.nil? || author == person || author.is_a_friend?(person) || person.already_request_friendship?(author)
          common_tags = tags & author.article_tags.keys
          if common_tags.size >= COMMON_TAGS
            suggestion = person.profile_suggestions.find_or_initialize_by_suggestion_id(author.id)
            suggestion.common_tags = common_tags.size
            suggestion.save
          end
        end
      end
    end
  end

  def self.communities_with_common_friends(person)
    community_attempts = 0
    person_friends = person.friends
    person_friends.each do |friend|
      friend.communities.each do |community|
        community_attempts += 1
        return unless person.profile_suggestions.count < N_SUGGESTIONS && community_attempts < MAX_ATTEMPTS
        unless person.is_member_of?(community) || community.already_request_membership?(person)
          common_friends = community.members & person.friends
          if common_friends.size >= COMMON_FRIENDS
            suggestion = person.profile_suggestions.find_or_initialize_by_suggestion_id(community.id)
            suggestion.common_friends = common_friends.size
            suggestion.save
          end
        end
      end
    end
  end

  def self.communities_with_common_tags(person)
    community_attempts = 0
    tags = person.article_tags.keys
    tags.each do |tag|
      community_attempts += 1
      return unless person.profile_suggestions.count < N_SUGGESTIONS && community_attempts < MAX_ATTEMPTS
      tagged_content = ActsAsTaggableOn::Tagging.includes(:taggable).where(:tag_id => ActsAsTaggableOn::Tag.find_by_name(tag))
      tagged_content.each do |tg|
        profile = tg.taggable.profile
        unless !profile.community? || person.is_member_of?(profile) || profile.already_request_membership?(person)
          common_tags = tags & profile.article_tags.keys
          if common_tags.size >= COMMON_TAGS
            suggestion = person.profile_suggestions.find_or_initialize_by_suggestion_id(profile.id)
            suggestion.common_tags = common_tags.size
            suggestion.save
          end
        end
      end
    end
  end

  def disable
    self.enabled = false
    self.save
  end

  def self.generate_all_profile_suggestions
    Delayed::Job.enqueue(ProfileSuggestion::GenerateAllJob.new) unless ProfileSuggestion::GenerateAllJob.exists?
  end

  def self.generate_profile_suggestions(person_id)
    Delayed::Job.enqueue ProfileSuggestionsJob.new(person_id) unless ProfileSuggestionsJob.exists?(person_id)
  end

  class GenerateAllJob
    def self.exists?
      Delayed::Job.by_handler("--- !ruby/object:ProfileSuggestion::GenerateAllJob {}\n").count > 0
    end

    def perform
      Person.find_each {|person| ProfileSuggestion.generate_profile_suggestions(person.id) }
    end
  end

end