Class: Sentiero::Analytics::FormCollector

Inherits:
Object
  • Object
show all
Includes:
Bounded, Events
Defined in:
lib/sentiero/analytics/collectors/form_collector.rb

Overview

Per-URL form interaction math. The single definition of what an "input" and a "submit" are in rrweb terms. Two completion semantics for two callers:

#completed_count — sessions with inputs where EVERY input segment was
submitted (strict; one abandoned segment disqualifies).
#submitted_count — sessions with ANY __form_submit event, regardless of
input timing.

Constant Summary collapse

SUBMIT_TAG =

Recorder tag for a document-level form submit.

"__form_submit"
DROP_OFF_LIMIT =

Output cap for the drop-off table.

50

Constants included from Events

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

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from Bounded

#bounded_fetch, #bounded_increment

Constructor Details

#initialize(max_fields: nil) ⇒ FormCollector

max_fields: nil unbounded; an Integer caps the fields hash, flipping #capped.



27
28
29
30
31
32
33
34
35
36
# File 'lib/sentiero/analytics/collectors/form_collector.rb', line 27

def initialize(max_fields: nil)
  @max_fields = max_fields
  @total_submits = 0
  @fields = {}            # [url, node_id] => field-stats hash
  @drop_off = Hash.new(0) # [url, node_id] => abandon count
  @started = {}           # session_id => true  (≥1 input event seen)
  @submitted = {}         # session_id => true  (≥1 submit event, any segment)
  @abandoned = {}         # session_id => true  (≥1 input segment not submitted)
  @capped = false
end

Instance Attribute Details

#cappedObject (readonly)

Returns the value of attribute capped.



24
25
26
# File 'lib/sentiero/analytics/collectors/form_collector.rb', line 24

def capped
  @capped
end

#total_submitsObject (readonly)

Returns the value of attribute total_submits.



24
25
26
# File 'lib/sentiero/analytics/collectors/form_collector.rb', line 24

def total_submits
  @total_submits
end

Instance Method Details

#collect(session_id, url, segment, labels: {}) ⇒ Object

Returns the count of input events found. labels: => label from the segment's DOM snapshot; {} omits them.



40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
# File 'lib/sentiero/analytics/collectors/form_collector.rb', line 40

def collect(session_id, url, segment, labels: {})
  @total_submits += segment.count { |e| submit?(e) }
  @submitted[session_id] = true if segment.any? { |e| submit?(e) }

  inputs = segment.select { |e| input?(e) }
  return 0 if inputs.empty?

  @started[session_id] = true
  record_fields(session_id, url, inputs, labels)

  first_input_at = inputs.first["timestamp"]
  unless (segment, first_input_at)
    @abandoned[session_id] = true
    @drop_off[[url, node_id(inputs.last)]] += 1
  end

  inputs.size
end

#completed_countObject

Sessions with inputs where NO input segment was abandoned; a submit on a later page never masks an abandonment.



70
71
72
# File 'lib/sentiero/analytics/collectors/form_collector.rb', line 70

def completed_count
  @started.count { |id, _| !@abandoned.key?(id) }
end

#started_countObject



59
60
61
# File 'lib/sentiero/analytics/collectors/form_collector.rb', line 59

def started_count
  @started.size
end

#submitted_countObject

Counts a submit on the target URL even when inputs landed on a prior segment.



64
65
66
# File 'lib/sentiero/analytics/collectors/form_collector.rb', line 64

def 
  @submitted.size
end

#summarize_drop_off(include_labels: false) ⇒ Object



90
91
92
93
94
95
96
97
98
99
100
101
102
# File 'lib/sentiero/analytics/collectors/form_collector.rb', line 90

def summarize_drop_off(include_labels: false)
  @drop_off
    .sort_by { |(url, id), count| [-count, url.to_s, id] }
    .first(DROP_OFF_LIMIT)
    .map do |(url, id), count|
      row = {}
      row[:field_id] = id
      row[:label] = @fields.key?([url, id]) ? @fields[[url, id]][:label] : nil if include_labels
      row[:url] = url
      row[:count] = count
      row
    end
end

#summarize_fields(started, include_labels: false) ⇒ Object



74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
# File 'lib/sentiero/analytics/collectors/form_collector.rb', line 74

def summarize_fields(started, include_labels: false)
  @fields
    .sort_by { |(url, id), stats| [-stats[:sessions], url.to_s, id] }
    .map do |(url, id), stats|
      row = {}
      row[:field_id] = id
      row[:label] = stats[:label] if include_labels
      row[:url] = url
      row[:sessions] = stats[:sessions]
      row[:completion_rate] = started.zero? ? 0.0 : stats[:sessions].to_f / started
      row[:avg_time_to_fill_ms] = stats[:units].zero? ? 0.0 : stats[:total_fill_ms] / stats[:units]
      row[:total_refills] = stats[:total_refills]
      row
    end
end