The code powering m.abunchtell.com https://m.abunchtell.com
選択できるのは25トピックまでです。 トピックは、先頭が英数字で、英数字とダッシュ('-')を使用した35文字以内のものにしてください。
 
 
 
 

78 行
2.6 KiB

  1. # frozen_string_literal: true
  2. class TrendingTags
  3. KEY = 'trending_tags'
  4. EXPIRE_HISTORY_AFTER = 7.days.seconds
  5. EXPIRE_TRENDS_AFTER = 1.day.seconds
  6. THRESHOLD = 5
  7. LIMIT = 10
  8. class << self
  9. include Redisable
  10. def record_use!(tag, account, at_time = Time.now.utc)
  11. return if account.silenced? || account.bot? || !tag.usable? || !(tag.trendable? || tag.requires_review?)
  12. increment_historical_use!(tag.id, at_time)
  13. increment_unique_use!(tag.id, account.id, at_time)
  14. increment_vote!(tag, at_time)
  15. end
  16. def get(limit, filtered: true)
  17. tag_ids = redis.zrevrange("#{KEY}:#{Time.now.utc.beginning_of_day.to_i}", 0, LIMIT - 1).map(&:to_i)
  18. tags = Tag.where(id: tag_ids)
  19. tags = tags.where(trendable: true) if filtered
  20. tags = tags.each_with_object({}) { |tag, h| h[tag.id] = tag }
  21. tag_ids.map { |tag_id| tags[tag_id] }.compact.take(limit)
  22. end
  23. def trending?(tag)
  24. rank = redis.zrevrank("#{KEY}:#{Time.now.utc.beginning_of_day.to_i}", tag.id)
  25. rank.present? && rank <= LIMIT
  26. end
  27. private
  28. def increment_historical_use!(tag_id, at_time)
  29. key = "activity:tags:#{tag_id}:#{at_time.beginning_of_day.to_i}"
  30. redis.incrby(key, 1)
  31. redis.expire(key, EXPIRE_HISTORY_AFTER)
  32. end
  33. def increment_unique_use!(tag_id, account_id, at_time)
  34. key = "activity:tags:#{tag_id}:#{at_time.beginning_of_day.to_i}:accounts"
  35. redis.pfadd(key, account_id)
  36. redis.expire(key, EXPIRE_HISTORY_AFTER)
  37. end
  38. def increment_vote!(tag, at_time)
  39. key = "#{KEY}:#{at_time.beginning_of_day.to_i}"
  40. expected = redis.pfcount("activity:tags:#{tag.id}:#{(at_time - 1.day).beginning_of_day.to_i}:accounts").to_f
  41. expected = 1.0 if expected.zero?
  42. observed = redis.pfcount("activity:tags:#{tag.id}:#{at_time.beginning_of_day.to_i}:accounts").to_f
  43. if expected > observed || observed < THRESHOLD
  44. redis.zrem(key, tag.id)
  45. else
  46. score = ((observed - expected)**2) / expected
  47. old_rank = redis.zrevrank(key, tag.id)
  48. redis.zadd(key, score, tag.id)
  49. request_review!(tag) if (old_rank.nil? || old_rank > LIMIT) && redis.zrevrank(key, tag.id) <= LIMIT && !tag.trendable? && tag.requires_review? && !tag.requested_review?
  50. end
  51. redis.expire(key, EXPIRE_TRENDS_AFTER)
  52. end
  53. def request_review!(tag)
  54. return unless Setting.trends
  55. tag.touch(:requested_review_at)
  56. User.staff.includes(:account).find_each { |u| AdminMailer.new_trending_tag(u.account, tag).deliver_later! if u.allows_trending_tag_emails? }
  57. end
  58. end
  59. end