Class: Sentiero::Analytics::FunnelAnalyzer

Inherits:
Analyzer
  • Object
show all
Defined in:
lib/sentiero/analytics/funnel_analyzer.rb

Overview

Custom-event funnel: ordered step conversion across sessions. A session reaches step N+1 only when an event with that tag occurs strictly after the step-N match. Greedy-earliest chain matching is optimal for subsequence reachability, so "how far did this session get" is exact.

Constant Summary collapse

ERROR_TAG =

Excluded as a funnel step; has its own ErrorDiscovery surface.

"error"
INTERNAL_TAG_PREFIX =

Prefix of recorder-internal annotation tags (__perf, __click, ...).

"__"
MAX_STEPS =
3
MAX_TAGS =
200
MAX_STEP_EVENTS_PER_SESSION =

Bounds per-session memory.

100
MAX_EXAMPLES_PER_STEP =
10

Constants included from Events

Events::CUSTOM, Events::INCREMENTAL, Events::META, Events::SOURCE_INPUT, Events::SOURCE_MOUSE_INTERACTION, Events::SOURCE_SCROLL

Instance Attribute Summary

Attributes inherited from Analyzer

#store

Class Method Summary collapse

Instance Method Summary collapse

Methods inherited from Analyzer

#initialize

Methods included from EntryAttribution

#earlier?, #first_meta_href, #same_origin?, #update_entry_candidate

Methods included from Bounded

#bounded_fetch, #bounded_increment

Methods included from Stats

#mean, #offset_ms, #percentile, #top_counts

Constructor Details

This class inherits a constructor from Sentiero::Analytics::Analyzer

Class Method Details

.internal_tag?(tag) ⇒ Boolean

Returns:

  • (Boolean)


28
29
30
# File 'lib/sentiero/analytics/funnel_analyzer.rb', line 28

def internal_tag?(tag)
  !tag.is_a?(String) || tag.empty? || tag.start_with?(INTERNAL_TAG_PREFIX) || tag == ERROR_TAG
end

.usable_steps(tags) ⇒ Object



32
33
34
# File 'lib/sentiero/analytics/funnel_analyzer.rb', line 32

def usable_steps(tags)
  Array(tags).reject { |tag| internal_tag?(tag) }.first(MAX_STEPS)
end

Instance Method Details

#analyze(steps = [], limit: nil, since: nil, until_time: nil) ⇒ Object

Fewer than 2 usable steps yields steps: [] but still collects vocabulary.



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
# File 'lib/sentiero/analytics/funnel_analyzer.rb', line 38

def analyze(steps = [], limit: nil, since: nil, until_time: nil)
  steps = self.class.usable_steps(steps)
  steps = [] if steps.size < 2
  step_set = steps.uniq

  tags = {}
  sessions = {}
  accumulation_capped = false

  _scanned, hit_cap = scan_sessions(limit: limit, since: since, until_time: until_time) do |summary, window_id, events|
    session_id = summary[:session_id]

    anchor = events.first&.fetch("timestamp", nil)
    events.each do |event|
      tag = custom_tag(event)
      next unless tag

      accumulation_capped = true unless tally_tag(tags, tag)
      next if steps.empty? || !step_set.include?(tag)
      next unless event["timestamp"].is_a?(Numeric)

      entries = sessions[session_id] ||= []
      if entries.size >= MAX_STEP_EVENTS_PER_SESSION
        accumulation_capped = true
        next
      end

      entries << {
        tag: tag,
        timestamp: event["timestamp"],
        window_id: window_id,
        offset_ms: offset_ms(anchor, event["timestamp"])
      }
    end
  end

  {
    tags: tags.keys.sort,
    steps: summarize_steps(steps, sessions),
    was_truncated: accumulation_capped || hit_cap
  }
end