Skip to content

Commit 768da80

Browse files
committed
include.empty_metrics
1 parent dd8f2d6 commit 768da80

5 files changed

Lines changed: 111 additions & 10 deletions

File tree

extra/lib/plausible/stats/goal/revenue.ex

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,10 @@ defmodule Plausible.Stats.Goal.Revenue do
5656
query.revenue_currencies[:default] ||
5757
get_goal_dimension_revenue_currency(query, dimension_values)
5858

59+
format_revenue_metric(value, currency)
60+
end
61+
62+
def format_revenue_metric(value, currency) do
5963
if currency do
6064
money = Money.new!(value || 0, currency)
6165

lib/plausible/stats/metrics.ex

Lines changed: 26 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ defmodule Plausible.Stats.Metrics do
77

88
use Plausible
99

10+
@revenue_metrics on_ee(do: Plausible.Stats.Goal.Revenue.revenue_metrics(), else: [])
11+
1012
@all_metrics [
1113
:visitors,
1214
:visits,
@@ -21,22 +23,36 @@ defmodule Plausible.Stats.Metrics do
2123
:time_on_page,
2224
:percentage,
2325
:scroll_depth
24-
] ++ on_ee(do: Plausible.Stats.Goal.Revenue.revenue_metrics(), else: [])
26+
] ++ @revenue_metrics
2527

2628
@metric_mappings Enum.into(@all_metrics, %{}, fn metric -> {to_string(metric), metric} end)
2729

2830
def metric?(value), do: Enum.member?(@all_metrics, value)
2931

3032
on_ee do
31-
def default_value(metric, query, dimensions)
32-
when metric in [:average_revenue, :total_revenue],
33-
do: Plausible.Stats.Goal.Revenue.format_revenue_metric(nil, query, dimensions)
33+
# Default value in a goal breakdown depends on per-row currency
34+
def default_value(metric, query, row_dimensions) when metric in @revenue_metrics do
35+
Plausible.Stats.Goal.Revenue.format_revenue_metric(nil, query, row_dimensions)
36+
end
37+
end
38+
39+
def default_value(metric, _query, _dimensions), do: default_value(metric)
40+
41+
on_ee do
42+
# When revenue metrics are queried without event:goal dimension,
43+
# a single default currency is expected.
44+
def default_value(metric, query) when metric in @revenue_metrics do
45+
currency = query.revenue_currencies.default
46+
Plausible.Stats.Goal.Revenue.format_revenue_metric(nil, currency)
47+
end
3448
end
3549

36-
def default_value(:visit_duration, _query, _dimensions), do: nil
37-
def default_value(:exit_rate, _query, _dimensions), do: nil
38-
def default_value(:scroll_depth, _query, _dimensions), do: nil
39-
def default_value(:time_on_page, _query, _dimensions), do: nil
50+
def default_value(metric, _query), do: default_value(metric)
51+
52+
def default_value(:visit_duration), do: nil
53+
def default_value(:exit_rate), do: nil
54+
def default_value(:scroll_depth), do: nil
55+
def default_value(:time_on_page), do: nil
4056

4157
@float_metrics [
4258
:views_per_visit,
@@ -45,8 +61,8 @@ defmodule Plausible.Stats.Metrics do
4561
:conversion_rate,
4662
:group_conversion_rate
4763
]
48-
def default_value(metric, _query, _dimensions) when metric in @float_metrics, do: 0.0
49-
def default_value(_metric, _query, _dimensions), do: 0
64+
def default_value(metric) when metric in @float_metrics, do: 0.0
65+
def default_value(_metric), do: 0
5066

5167
def from_string!(str) do
5268
Map.fetch!(@metric_mappings, str)

lib/plausible/stats/query_include.ex

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@ defmodule Plausible.Stats.QueryInclude do
99
# breakdowns by multiple dimensions (time + non-time). Also, at this point it is
1010
# still unclear whether `time_labels` will stay in the public API or not.
1111
time_label_result_indices: false,
12+
# Another flag to simplify frontend code by not having to repeat the logic defining
13+
# default values for metrics (especially revenue metrics).
14+
empty_metrics: false,
1215
present_index: false,
1316
partial_time_labels: false,
1417
total_rows: false,
@@ -27,6 +30,7 @@ defmodule Plausible.Stats.QueryInclude do
2730
imports_meta: boolean(),
2831
time_labels: boolean(),
2932
time_label_result_indices: boolean(),
33+
empty_metrics: boolean(),
3034
present_index: boolean(),
3135
partial_time_labels: boolean(),
3236
total_rows: boolean(),

lib/plausible/stats/query_result.ex

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ defmodule Plausible.Stats.QueryResult do
5858
%{}
5959
|> add_imports_meta(runner.main_query)
6060
|> add_metric_warnings_meta(runner.main_query)
61+
|> add_empty_metrics_meta(runner.main_query)
6162
|> add_time_labels_meta(runner)
6263
|> add_time_labels_result_indices_meta(runner)
6364
|> add_comparison_time_labels_meta(runner)
@@ -92,6 +93,18 @@ defmodule Plausible.Stats.QueryResult do
9293
end
9394
end
9495

96+
defp add_empty_metrics_meta(meta, query) do
97+
if query.include.empty_metrics and "event:goal" not in query.dimensions do
98+
Map.put(
99+
meta,
100+
:empty_metrics,
101+
Enum.map(query.metrics, &Plausible.Stats.Metrics.default_value(&1, query))
102+
)
103+
else
104+
meta
105+
end
106+
end
107+
95108
defp add_time_labels_meta(meta, %QueryRunner{main_query: query}) do
96109
if query.include.time_labels do
97110
Map.put(meta, :time_labels, Plausible.Stats.Time.time_labels(query))

test/plausible/stats/query/query_test.exs

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -464,4 +464,68 @@ defmodule Plausible.Stats.QueryTest do
464464
assert [%{dimensions: ["2021-01-01 00:05:00"], metrics: [1, 50.0]}] = results
465465
end
466466
end
467+
468+
describe "include.empty_metrics" do
469+
test "if not asked for, no empty_metrics are returned under meta", %{site: site} do
470+
{:ok, query} =
471+
QueryBuilder.build(site, %ParsedQueryParams{
472+
metrics: [:visitors],
473+
input_date_range: :all
474+
})
475+
476+
%Stats.QueryResult{meta: meta} = Stats.query(site, query)
477+
478+
assert is_nil(meta[:empty_metrics])
479+
end
480+
481+
test "for regular metrics", %{site: site} do
482+
{:ok, query} =
483+
QueryBuilder.build(site, %ParsedQueryParams{
484+
metrics: [:visitors, :bounce_rate, :scroll_depth],
485+
input_date_range: :all,
486+
include: %QueryInclude{empty_metrics: true},
487+
filters: [[:is, "event:page", ["/"]]]
488+
})
489+
490+
%Stats.QueryResult{meta: meta} = Stats.query(site, query)
491+
492+
assert meta[:empty_metrics] == [0, 0.0, nil]
493+
end
494+
495+
@tag :ee_only
496+
test "for revenue metrics", %{site: site} do
497+
insert(:goal, site: site, event_name: "Purchase", currency: "EUR")
498+
499+
{:ok, query} =
500+
QueryBuilder.build(site, %ParsedQueryParams{
501+
metrics: [:average_revenue, :total_revenue],
502+
input_date_range: :all,
503+
include: %QueryInclude{empty_metrics: true},
504+
filters: [[:is, "event:goal", ["Purchase"]]]
505+
})
506+
507+
%Stats.QueryResult{meta: meta} = Stats.query(site, query)
508+
509+
assert meta[:empty_metrics] == [
510+
%{currency: :EUR, long: "€0.00", short: "€0.0", value: 0.0},
511+
%{currency: :EUR, long: "€0.00", short: "€0.0", value: 0.0}
512+
]
513+
end
514+
515+
test "is ignored when event:goal dimension used", %{site: site} do
516+
insert(:goal, site: site, event_name: "Purchase", currency: "EUR")
517+
518+
{:ok, query} =
519+
QueryBuilder.build(site, %ParsedQueryParams{
520+
metrics: [:visitors, :average_revenue],
521+
input_date_range: :all,
522+
include: %QueryInclude{empty_metrics: true},
523+
dimensions: ["event:goal"]
524+
})
525+
526+
%Stats.QueryResult{meta: meta} = Stats.query(site, query)
527+
528+
assert is_nil(meta[:empty_metrics])
529+
end
530+
end
467531
end

0 commit comments

Comments
 (0)