You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

100 lines
2.0 KiB

  1. # frozen_string_literal: true
  2. class Trends::History
  3. include Enumerable
  4. class Aggregate
  5. include Redisable
  6. def initialize(prefix, id, date_range)
  7. @days = date_range.map { |date| Day.new(prefix, id, date.to_time(:utc)) }
  8. end
  9. def uses
  10. with_redis { |redis| redis.mget(*@days.map { |day| day.key_for(:uses) }).map(&:to_i).sum }
  11. end
  12. def accounts
  13. with_redis { |redis| redis.pfcount(*@days.map { |day| day.key_for(:accounts) }) }
  14. end
  15. end
  16. class Day
  17. include Redisable
  18. EXPIRE_AFTER = 14.days.seconds
  19. def initialize(prefix, id, day)
  20. @prefix = prefix
  21. @id = id
  22. @day = day.beginning_of_day
  23. end
  24. attr_reader :day
  25. def accounts
  26. with_redis { |redis| redis.pfcount(key_for(:accounts)) }
  27. end
  28. def uses
  29. with_redis { |redis| redis.get(key_for(:uses))&.to_i || 0 }
  30. end
  31. def add(account_id)
  32. with_redis do |redis|
  33. redis.pipelined do |pipeline|
  34. pipeline.incrby(key_for(:uses), 1)
  35. pipeline.pfadd(key_for(:accounts), account_id)
  36. pipeline.expire(key_for(:uses), EXPIRE_AFTER)
  37. pipeline.expire(key_for(:accounts), EXPIRE_AFTER)
  38. end
  39. end
  40. end
  41. def as_json
  42. { day: day.to_i.to_s, accounts: accounts.to_s, uses: uses.to_s }
  43. end
  44. def key_for(suffix)
  45. case suffix
  46. when :accounts
  47. "#{key_prefix}:#{suffix}"
  48. when :uses
  49. key_prefix
  50. end
  51. end
  52. def key_prefix
  53. "activity:#{@prefix}:#{@id}:#{day.to_i}"
  54. end
  55. end
  56. def initialize(prefix, id)
  57. @prefix = prefix
  58. @id = id
  59. end
  60. def get(date)
  61. Day.new(@prefix, @id, date)
  62. end
  63. def add(account_id, at_time = Time.now.utc)
  64. Day.new(@prefix, @id, at_time).add(account_id)
  65. end
  66. def aggregate(date_range)
  67. Aggregate.new(@prefix, @id, date_range)
  68. end
  69. def each(&block)
  70. if block_given?
  71. (0...7).map { |i| block.call(get(i.days.ago)) }
  72. else
  73. to_enum(:each)
  74. end
  75. end
  76. def as_json(*)
  77. map(&:as_json)
  78. end
  79. end