about summary refs log tree commit diff
path: root/app/models/trends/history.rb
blob: 74723e35c99c963152b8f206d574f033f309392b (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
# frozen_string_literal: true

class Trends::History
  include Enumerable

  class Aggregate
    include Redisable

    def initialize(prefix, id, date_range)
      @days = date_range.map { |date| Day.new(prefix, id, date.to_time(:utc)) }
    end

    def uses
      with_redis { |redis| redis.mget(*@days.map { |day| day.key_for(:uses) }).map(&:to_i).sum }
    end

    def accounts
      with_redis { |redis| redis.pfcount(*@days.map { |day| day.key_for(:accounts) }) }
    end
  end

  class Day
    include Redisable

    EXPIRE_AFTER = 14.days.seconds

    def initialize(prefix, id, day)
      @prefix = prefix
      @id     = id
      @day    = day.beginning_of_day
    end

    attr_reader :day

    def accounts
      with_redis { |redis| redis.pfcount(key_for(:accounts)) }
    end

    def uses
      with_redis { |redis| redis.get(key_for(:uses))&.to_i || 0 }
    end

    def add(account_id)
      with_redis do |redis|
        redis.pipelined do |pipeline|
          pipeline.incrby(key_for(:uses), 1)
          pipeline.pfadd(key_for(:accounts), account_id)
          pipeline.expire(key_for(:uses), EXPIRE_AFTER)
          pipeline.expire(key_for(:accounts), EXPIRE_AFTER)
        end
      end
    end

    def as_json
      { day: day.to_i.to_s, accounts: accounts.to_s, uses: uses.to_s }
    end

    def key_for(suffix)
      case suffix
      when :accounts
        "#{key_prefix}:#{suffix}"
      when :uses
        key_prefix
      end
    end

    def key_prefix
      "activity:#{@prefix}:#{@id}:#{day.to_i}"
    end
  end

  def initialize(prefix, id)
    @prefix = prefix
    @id     = id
  end

  def get(date)
    Day.new(@prefix, @id, date)
  end

  def add(account_id, at_time = Time.now.utc)
    Day.new(@prefix, @id, at_time).add(account_id)
  end

  def aggregate(date_range)
    Aggregate.new(@prefix, @id, date_range)
  end

  def each(&block)
    if block_given?
      (0...7).map { |i| block.call(get(i.days.ago)) }
    else
      to_enum(:each)
    end
  end

  def as_json(*)
    map(&:as_json)
  end
end