Class: Sentiero::Analytics::FrustrationAnalyzer

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

Overview

Cross-session frustration signals per page URL: rage clicks (bursts at the same spot) and dead clicks (clicks the page never responds to), plus top rage-clicked elements and per-incident replay links.

Detection itself lives in Frustration::Detectors (pure Ruby ports of the JS detectors, frontend/src/dashboard/frustration.js, pinned by ported tests so the two can't drift). Over the detectors' raw dead clicks this class layers cross-session aggregation and a de-noising pass: an app-level custom event in the dead window counts as a page response; the final click of a segment navigated away from is withdrawn; an error-coincident dead click is kept and tagged kind: "error".

Constant Summary collapse

CLICK_TAG =

Custom-event tag carrying the clicked element's CSS selector.

"__click"
INTERNAL_TAG_PREFIX =

Recorder-internal annotation prefix and the browser JS-error tag; neither proves the page responded to a click.

"__"
ERROR_TAG =
"error"
NEAREST_CLICK_TOLERANCE_MS =

Max ms a "__click" annotation may sit from a rage cluster's first click and still be attributed to it.

500
MAX_URLS =

Accumulation caps during the scan (sessions scan newest-first).

200
MAX_SELECTORS_PER_URL =
200
MAX_INCIDENTS_PER_URL =
20
TOP_SELECTORS_LIMIT =
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

.detect_frustration_events(events) ⇒ Object

Stable entry point for callers outside this class (EngagementAnalyzer, PageReportAnalyzer) that only need raw detection, not the cross-session aggregation below.



41
# File 'lib/sentiero/analytics/frustration_analyzer.rb', line 41

def self.detect_frustration_events(events) = Frustration::Detectors.detect_frustration_events(events)

Instance Method Details

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

Detectors run over the FULL window (their response semantics span page boundaries by design); each incident is then attributed to the page segment its click happened on.



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

def analyze(limit: nil, since: nil, until_time: nil)
  pages = {}
  accumulation_capped = false

  _scanned, hit_cap = scan_sessions(limit: limit, since: since, until_time: until_time) do |summary, window_id, events|
    incidents = Frustration::Detectors.detect_frustration_events(events)
    next if incidents.empty?

    segments = page_segments(events)
    incidents = refine_incidents(incidents, segments)
    next if incidents.empty?

    annotations = click_annotations(events)

    incidents.each do |incident|
      page = page_for(pages, incident[:url])
      unless page
        accumulation_capped = true
        next
      end

      accumulation_capped = true unless record_incident(page, incident, summary[:session_id], window_id, annotations)
      page[:session_ids][summary[:session_id]] = true
    end
  end

  {
    pages: pages.transform_values { |page| summarize(page) },
    was_truncated: accumulation_capped || hit_cap
  }
end