Skip to content

Commit fd72fb8

Browse files
authored
Implement conversion rate and rate step calculation in exploration funnel API (#6250)
* Implement conversion rate and rate step calculation in exploration funnel API * Use conversion rate for bar graphs and make next step relative to funnel * Compute bar graph relative to max visitors in suggestions in backwards dir
1 parent f8f970b commit fd72fb8

File tree

4 files changed

+60
-7
lines changed

4 files changed

+60
-7
lines changed

assets/js/dashboard/stats/exploration.js

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -54,8 +54,10 @@ function fetchFunnelData(site, dashboardState, steps, direction) {
5454
function ExplorationColumn({
5555
header,
5656
steps,
57+
maxVisitors,
5758
selected,
5859
selectedVisitors,
60+
selectedConversionRate,
5961
onSelect,
6062
dashboardState,
6163
direction
@@ -113,7 +115,10 @@ function ExplorationColumn({
113115
// eslint-disable-next-line react-hooks/exhaustive-deps
114116
}, [dashboardState, stepsFingerprint, filter, direction, site, selected])
115117

116-
const maxVisitors = results.length > 0 ? results[0].visitors : 1
118+
const stepMaxVisitors =
119+
direction === EXPLORATION_DIRECTIONS.BACKWARD
120+
? results[0]?.visitors
121+
: maxVisitors || results[0]?.visitors
117122

118123
return (
119124
<div className="flex-1 min-w-0 border border-gray-200 dark:border-gray-700 rounded-lg overflow-hidden">
@@ -170,7 +175,10 @@ function ExplorationColumn({
170175
isSelected && selectedVisitors !== null
171176
? selectedVisitors
172177
: visitors
173-
const pct = Math.round((visitorsToShow / maxVisitors) * 100)
178+
const conversionRateToShow =
179+
isSelected && selectedConversionRate !== null
180+
? selectedConversionRate
181+
: Math.round((visitors / stepMaxVisitors) * 100)
174182

175183
return (
176184
<li key={label}>
@@ -204,7 +212,7 @@ function ExplorationColumn({
204212
? 'bg-indigo-500'
205213
: 'bg-indigo-300 dark:bg-indigo-600'
206214
}`}
207-
style={{ width: `${pct}%` }}
215+
style={{ width: `${conversionRateToShow}%` }}
208216
/>
209217
</div>
210218
</button>
@@ -319,6 +327,8 @@ export function FunnelExploration() {
319327
steps={steps.length >= i ? steps.slice(0, i) : null}
320328
selected={steps[i] || null}
321329
selectedVisitors={funnel[i]?.visitors ?? null}
330+
selectedConversionRate={funnel[i]?.conversion_rate ?? null}
331+
maxVisitors={funnel[0]?.visitors ?? null}
322332
onSelect={(selected) => handleSelect(i, selected)}
323333
dashboardState={dashboardState}
324334
direction={direction}

lib/plausible/stats/exploration.ex

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,9 @@ defmodule Plausible.Stats.Exploration do
3434
step: Journey.Step.t(),
3535
visitors: non_neg_integer(),
3636
dropoff: non_neg_integer(),
37-
dropoff_percentage: String.t()
37+
dropoff_percentage: String.t(),
38+
conversion_rate: String.t(),
39+
conversion_rate_step: String.t()
3840
}
3941

4042
@spec next_steps(Query.t(), journey(), String.t(), direction()) ::
@@ -219,25 +221,36 @@ defmodule Plausible.Stats.Exploration do
219221
defp to_funnel([result], journey) do
220222
journey
221223
|> Enum.with_index()
222-
|> Enum.reduce(%{funnel: [], visitors_at_previous: nil}, fn {step, idx}, acc ->
224+
|> Enum.reduce(%{funnel: [], visitors_at_previous: nil, total_visitors: nil}, fn {step, idx},
225+
acc ->
223226
current_visitors = Map.get(result, idx + 1, 0)
227+
total_visitors = acc.total_visitors || current_visitors
224228

225229
dropoff =
226230
if acc.visitors_at_previous, do: acc.visitors_at_previous - current_visitors, else: 0
227231

228232
dropoff_percentage = percentage(dropoff, acc.visitors_at_previous)
233+
conversion_rate = percentage(current_visitors, total_visitors)
234+
conversion_rate_step = percentage(current_visitors, acc.visitors_at_previous)
229235

230236
funnel = [
231237
%{
232238
step: step,
233239
visitors: current_visitors,
234240
dropoff: dropoff,
235-
dropoff_percentage: dropoff_percentage
241+
dropoff_percentage: dropoff_percentage,
242+
conversion_rate: conversion_rate,
243+
conversion_rate_step: conversion_rate_step
236244
}
237245
| acc.funnel
238246
]
239247

240-
%{acc | funnel: funnel, visitors_at_previous: current_visitors}
248+
%{
249+
acc
250+
| funnel: funnel,
251+
visitors_at_previous: current_visitors,
252+
total_visitors: total_visitors
253+
}
241254
end)
242255
|> Map.fetch!(:funnel)
243256
|> Enum.reverse()

test/plausible/stats/exploration_test.exs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,14 +85,20 @@ defmodule Plausible.Stats.ExplorationTest do
8585
assert step1.visitors == 2
8686
assert step1.dropoff == 0
8787
assert step1.dropoff_percentage == "0"
88+
assert step1.conversion_rate == "100"
89+
assert step1.conversion_rate_step == "0"
8890
assert step2.step.pathname == "/login"
8991
assert step2.visitors == 2
9092
assert step2.dropoff == 0
9193
assert step2.dropoff_percentage == "0"
94+
assert step2.conversion_rate == "100"
95+
assert step2.conversion_rate_step == "100"
9296
assert step3.step.pathname == "/logout"
9397
assert step3.visitors == 1
9498
assert step3.dropoff == 1
9599
assert step3.dropoff_percentage == "50"
100+
assert step3.conversion_rate == "50"
101+
assert step3.conversion_rate_step == "50"
96102
end
97103

98104
test "respects filters in the query", %{site: site} do
@@ -114,14 +120,20 @@ defmodule Plausible.Stats.ExplorationTest do
114120
assert step1.visitors == 1
115121
assert step1.dropoff == 0
116122
assert step1.dropoff_percentage == "0"
123+
assert step1.conversion_rate == "100"
124+
assert step1.conversion_rate_step == "0"
117125
assert step2.step.pathname == "/login"
118126
assert step2.visitors == 1
119127
assert step2.dropoff == 0
120128
assert step2.dropoff_percentage == "0"
129+
assert step2.conversion_rate == "100"
130+
assert step2.conversion_rate_step == "100"
121131
assert step3.step.pathname == "/logout"
122132
assert step3.visitors == 0
123133
assert step3.dropoff == 1
124134
assert step3.dropoff_percentage == "100"
135+
assert step3.conversion_rate == "0"
136+
assert step3.conversion_rate_step == "0"
125137
end
126138

127139
test "returns error on empty journey", %{site: site} do
@@ -146,14 +158,20 @@ defmodule Plausible.Stats.ExplorationTest do
146158
assert step1.visitors == 1
147159
assert step1.dropoff == 1
148160
assert step1.dropoff_percentage == "50"
161+
assert step1.conversion_rate == "50"
162+
assert step1.conversion_rate_step == "50"
149163
assert step2.step.pathname == "/login"
150164
assert step2.visitors == 2
151165
assert step2.dropoff == 0
152166
assert step2.dropoff_percentage == "0"
167+
assert step2.conversion_rate == "100"
168+
assert step2.conversion_rate_step == "100"
153169
assert step3.step.pathname == "/home"
154170
assert step3.visitors == 2
155171
assert step3.dropoff == 0
156172
assert step3.dropoff_percentage == "0"
173+
assert step3.conversion_rate == "100"
174+
assert step3.conversion_rate_step == "0"
157175
end
158176
end
159177

test/plausible_web/controllers/api/stats_controller/exploration_test.exs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,14 +150,20 @@ defmodule PlausibleWeb.Api.StatsController.ExplorationTest do
150150
assert step1["visitors"] == 2
151151
assert step1["dropoff"] == 0
152152
assert step1["dropoff_percentage"] == "0"
153+
assert step1["conversion_rate"] == "100"
154+
assert step1["conversion_rate_step"] == "0"
153155
assert step2["step"]["pathname"] == "/login"
154156
assert step2["visitors"] == 2
155157
assert step2["dropoff"] == 0
156158
assert step2["dropoff_percentage"] == "0"
159+
assert step2["conversion_rate"] == "100"
160+
assert step2["conversion_rate_step"] == "100"
157161
assert step3["step"]["pathname"] == "/logout"
158162
assert step3["visitors"] == 1
159163
assert step3["dropoff"] == 1
160164
assert step3["dropoff_percentage"] == "50"
165+
assert step3["conversion_rate"] == "50"
166+
assert step3["conversion_rate_step"] == "50"
161167
end
162168

163169
test "it supports backward direction", %{conn: conn, site: site} do
@@ -183,14 +189,20 @@ defmodule PlausibleWeb.Api.StatsController.ExplorationTest do
183189
assert step1["visitors"] == 1
184190
assert step1["dropoff"] == 1
185191
assert step1["dropoff_percentage"] == "50"
192+
assert step1["conversion_rate"] == "50"
193+
assert step1["conversion_rate_step"] == "50"
186194
assert step2["step"]["pathname"] == "/login"
187195
assert step2["visitors"] == 2
188196
assert step2["dropoff"] == 0
189197
assert step2["dropoff_percentage"] == "0"
198+
assert step2["conversion_rate"] == "100"
199+
assert step2["conversion_rate_step"] == "100"
190200
assert step3["step"]["pathname"] == "/home"
191201
assert step3["visitors"] == 2
192202
assert step3["dropoff"] == 0
193203
assert step3["dropoff_percentage"] == "0"
204+
assert step3["conversion_rate"] == "100"
205+
assert step3["conversion_rate_step"] == "0"
194206
end
195207
end
196208
end

0 commit comments

Comments
 (0)