Class: Sentiero::Analytics::EngagementAnalyzer

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

Overview

Scores each session 0–100 on a STRUGGLE score (higher = more friction): a weighted blend of eight signals, each saturating to 0.0..1.0 so one pathological session can't dominate. Signals: rage_clicks, dead_clicks, nav_churn, idle_ratio, thrashing_scroll, quick_bounce, form_refills, error_abandonment.

Constant Summary collapse

WEIGHTS =

Signal weights; MUST sum to 1.00.

{
  rage_clicks: 0.20,
  dead_clicks: 0.15,
  nav_churn: 0.15,
  idle_ratio: 0.10,
  thrashing_scroll: 0.10,
  quick_bounce: 0.10,
  form_refills: 0.10,
  error_abandonment: 0.10
}.freeze
RAGE_SATURATION =

3+ rage clusters → full rage sub-score

3
DEAD_SATURATION =

3+ dead clicks → full dead sub-score

3
3
THRASH_SATURATION =

3+ scroll reversals → full thrash sub-score

3
REFILL_SATURATION =

2+ field re-fills → full refill sub-score

2
IDLE_GAP_MS =

consecutive events farther apart than this are "idle"

10_000
THRASH_MIN_DELTA_PX =

a scroll delta below this is too small to be thrashing

100
THRASH_WINDOW_MS =

both deltas of a reversal must fall within this span

1_000
QUICK_BOUNCE_MS =

single-page sessions shorter than this bounced

5_000
ERROR_ABANDON_MS =

a JS error within this of the session end = abandonment

8_000
MAX_SESSIONS =

display cap on returned rows (does NOT set was_truncated)

500
DISTRIBUTION_BINS =
%w[0-20 20-40 40-60 60-80 80-100].freeze
ERROR_TAG =
"error"
"navigation"

Constants included from Events

Sentiero::Analytics::Events::CUSTOM, Sentiero::Analytics::Events::INCREMENTAL, Sentiero::Analytics::Events::META, Sentiero::Analytics::Events::SOURCE_INPUT, Sentiero::Analytics::Events::SOURCE_MOUSE_INTERACTION, Sentiero::Analytics::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

.bin_for(score) ⇒ Object

Integer division pins boundaries: 19→"0-20", 20→"20-40", 100→"80-100".



43
44
45
# File 'lib/sentiero/analytics/engagement_analyzer.rb', line 43

def self.bin_for(score)
  DISTRIBUTION_BINS[[(score / 20), 4].min]
end

Instance Method Details

#analyze(limit: nil, since: nil, until_time: nil) ⇒ Object

The MAX_SESSIONS row cap is a DISPLAY bound (keeps highest scores, does NOT set was_truncated); only the scan cap sets was_truncated.



49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
# File 'lib/sentiero/analytics/engagement_analyzer.rb', line 49

def analyze(limit: nil, since: nil, until_time: nil)
  accumulators = {}

  scanned, hit_cap = scan_sessions(limit: limit, since: since, until_time: until_time) do |summary, window_id, events|
    session_id = summary[:session_id]
    acc = (accumulators[session_id] ||= new_accumulator(summary, window_id))
    accumulate_window(acc, events)
  end

  rows = accumulators.values.map { |acc| score_session(acc) }
  distribution = build_distribution(rows)
  rows.sort_by! { |row| [-row[:score], row[:session_id]] }

  {
    sessions: rows.first(MAX_SESSIONS),
    distribution: distribution,
    scanned: scanned,
    was_truncated: hit_cap
  }
end