mirror of
https://github.com/plausible/analytics.git
synced 2025-01-03 15:17:58 +03:00
Add revenue metrics to top stats (#3059)
This commit adds revenue data to top stats. Average and total are displayed when filtering by a revenue goal (or many if they have the same currency set).
This commit is contained in:
parent
d8543c81cc
commit
b97290b5cf
@ -19,13 +19,13 @@ export default class TopStats extends React.Component {
|
|||||||
const formattedComparison = numberFormatter(Math.abs(comparison))
|
const formattedComparison = numberFormatter(Math.abs(comparison))
|
||||||
|
|
||||||
const defaultClassName = classNames({
|
const defaultClassName = classNames({
|
||||||
"text-xs dark:text-gray-100": !forceDarkBg,
|
"pl-2 text-xs dark:text-gray-100": !forceDarkBg,
|
||||||
"text-xs text-gray-100": forceDarkBg
|
"pl-2 text-xs text-gray-100": forceDarkBg
|
||||||
})
|
})
|
||||||
|
|
||||||
const noChangeClassName = classNames({
|
const noChangeClassName = classNames({
|
||||||
"text-xs text-gray-700 dark:text-gray-300": !forceDarkBg,
|
"pl-2 text-xs text-gray-700 dark:text-gray-300": !forceDarkBg,
|
||||||
"text-xs text-gray-300": forceDarkBg
|
"pl-2 text-xs text-gray-300": forceDarkBg
|
||||||
})
|
})
|
||||||
|
|
||||||
if (comparison > 0) {
|
if (comparison > 0) {
|
||||||
@ -46,6 +46,8 @@ export default class TopStats extends React.Component {
|
|||||||
return durationFormatter(value)
|
return durationFormatter(value)
|
||||||
} else if (['bounce rate', 'conversion rate'].includes(name.toLowerCase())) {
|
} else if (['bounce rate', 'conversion rate'].includes(name.toLowerCase())) {
|
||||||
return value + '%'
|
return value + '%'
|
||||||
|
} else if (['average revenue', 'total revenue'].includes(name.toLowerCase())) {
|
||||||
|
return value?.short
|
||||||
} else {
|
} else {
|
||||||
return numberFormatter(value)
|
return numberFormatter(value)
|
||||||
}
|
}
|
||||||
@ -56,6 +58,8 @@ export default class TopStats extends React.Component {
|
|||||||
return durationFormatter(value)
|
return durationFormatter(value)
|
||||||
} else if (['bounce rate', 'conversion rate'].includes(name.toLowerCase())) {
|
} else if (['bounce rate', 'conversion rate'].includes(name.toLowerCase())) {
|
||||||
return value + '%'
|
return value + '%'
|
||||||
|
} else if (['average revenue', 'total revenue'].includes(name.toLowerCase())) {
|
||||||
|
return value?.long
|
||||||
} else {
|
} else {
|
||||||
return (value || 0).toLocaleString()
|
return (value || 0).toLocaleString()
|
||||||
}
|
}
|
||||||
|
@ -2,11 +2,26 @@ defmodule Plausible.Stats.Aggregate do
|
|||||||
alias Plausible.Stats.Query
|
alias Plausible.Stats.Query
|
||||||
use Plausible.ClickhouseRepo
|
use Plausible.ClickhouseRepo
|
||||||
import Plausible.Stats.{Base, Imported, Util}
|
import Plausible.Stats.{Base, Imported, Util}
|
||||||
|
import Ecto.Query
|
||||||
|
|
||||||
@event_metrics [:visitors, :pageviews, :events, :sample_percent]
|
@event_metrics [
|
||||||
|
:visitors,
|
||||||
|
:pageviews,
|
||||||
|
:events,
|
||||||
|
:sample_percent,
|
||||||
|
:average_revenue,
|
||||||
|
:total_revenue
|
||||||
|
]
|
||||||
@session_metrics [:visits, :bounce_rate, :visit_duration, :views_per_visit, :sample_percent]
|
@session_metrics [:visits, :bounce_rate, :visit_duration, :views_per_visit, :sample_percent]
|
||||||
|
@revenue_metrics [:average_revenue, :total_revenue]
|
||||||
|
|
||||||
def aggregate(site, query, metrics) do
|
def aggregate(site, query, metrics) do
|
||||||
|
# Aggregating revenue data works only for same currency goals. If the query
|
||||||
|
# is filtered by goals with different currencies, for example, one USD and
|
||||||
|
# other EUR, revenue metrics are dropped.
|
||||||
|
currency = get_revenue_tracking_currency(site, query, metrics)
|
||||||
|
metrics = if currency, do: metrics, else: metrics -- @revenue_metrics
|
||||||
|
|
||||||
event_metrics = Enum.filter(metrics, &(&1 in @event_metrics))
|
event_metrics = Enum.filter(metrics, &(&1 in @event_metrics))
|
||||||
event_task = fn -> aggregate_events(site, query, event_metrics) end
|
event_task = fn -> aggregate_events(site, query, event_metrics) end
|
||||||
session_metrics = Enum.filter(metrics, &(&1 in @session_metrics))
|
session_metrics = Enum.filter(metrics, &(&1 in @session_metrics))
|
||||||
@ -22,6 +37,7 @@ defmodule Plausible.Stats.Aggregate do
|
|||||||
Plausible.ClickhouseRepo.parallel_tasks([session_task, event_task, time_on_page_task])
|
Plausible.ClickhouseRepo.parallel_tasks([session_task, event_task, time_on_page_task])
|
||||||
|> Enum.reduce(%{}, fn aggregate, task_result -> Map.merge(aggregate, task_result) end)
|
|> Enum.reduce(%{}, fn aggregate, task_result -> Map.merge(aggregate, task_result) end)
|
||||||
|> Enum.map(&maybe_round_value/1)
|
|> Enum.map(&maybe_round_value/1)
|
||||||
|
|> Enum.map(&cast_revenue_metric_to_money(&1, currency))
|
||||||
|> Enum.map(fn {metric, value} -> {metric, %{value: value}} end)
|
|> Enum.map(fn {metric, value} -> {metric, %{value: value}} end)
|
||||||
|> Enum.into(%{})
|
|> Enum.into(%{})
|
||||||
end
|
end
|
||||||
@ -134,4 +150,37 @@ defmodule Plausible.Stats.Aggregate do
|
|||||||
end
|
end
|
||||||
|
|
||||||
defp maybe_round_value(entry), do: entry
|
defp maybe_round_value(entry), do: entry
|
||||||
|
|
||||||
|
defp get_revenue_tracking_currency(site, query, metrics) do
|
||||||
|
goal_filters =
|
||||||
|
case query.filters do
|
||||||
|
%{"event:goal" => {:is, {_, goal_name}}} -> [goal_name]
|
||||||
|
%{"event:goal" => {:member, list}} -> Enum.map(list, fn {_, goal_name} -> goal_name end)
|
||||||
|
_any -> []
|
||||||
|
end
|
||||||
|
|
||||||
|
if Enum.any?(metrics, &(&1 in @revenue_metrics)) && Enum.any?(goal_filters) do
|
||||||
|
revenue_goals_currencies =
|
||||||
|
Plausible.Repo.all(
|
||||||
|
from rg in assoc(site, :revenue_goals),
|
||||||
|
where: rg.event_name in ^goal_filters,
|
||||||
|
select: rg.currency,
|
||||||
|
distinct: true
|
||||||
|
)
|
||||||
|
|
||||||
|
if length(revenue_goals_currencies) == 1,
|
||||||
|
do: List.first(revenue_goals_currencies),
|
||||||
|
else: nil
|
||||||
|
else
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp cast_revenue_metric_to_money({metric, value}, currency) do
|
||||||
|
if metric in @revenue_metrics and is_atom(currency) do
|
||||||
|
{metric, Money.new!(value, currency)}
|
||||||
|
else
|
||||||
|
{metric, value}
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
@ -329,54 +329,61 @@ defmodule PlausibleWeb.Api.StatsController do
|
|||||||
{stats, 100}
|
{stats, 100}
|
||||||
end
|
end
|
||||||
|
|
||||||
defp fetch_top_stats(site, %Query{filters: %{"event:goal" => _goal}} = query, comparison_query) do
|
defp fetch_top_stats(site, %Query{filters: %{"event:goal" => _}} = query, comparison_query) do
|
||||||
totals_query = Query.remove_event_filters(query, [:goal, :props])
|
query_without_filters = Query.remove_event_filters(query, [:goal, :props])
|
||||||
|
metrics = [:visitors, :events, :average_revenue, :total_revenue]
|
||||||
|
|
||||||
%{
|
results_without_filters =
|
||||||
visitors: %{value: unique_visitors}
|
site
|
||||||
} = Stats.aggregate(site, totals_query, [:visitors])
|
|> Stats.aggregate(query_without_filters, [:visitors])
|
||||||
|
|> transform_keys(%{visitors: :unique_visitors})
|
||||||
|
|
||||||
%{
|
results =
|
||||||
visitors: %{value: converted_visitors},
|
site
|
||||||
events: %{value: completions}
|
|> Stats.aggregate(query, metrics)
|
||||||
} = Stats.aggregate(site, query, [:visitors, :events])
|
|> transform_keys(%{visitors: :converted_visitors, events: :completions})
|
||||||
|
|> Map.merge(results_without_filters)
|
||||||
|
|
||||||
{prev_unique_visitors, prev_converted_visitors, prev_completions} =
|
comparison =
|
||||||
if comparison_query do
|
if comparison_query do
|
||||||
totals_comparison_query = Query.remove_event_filters(comparison_query, [:goal, :props])
|
comparison_query_without_filters =
|
||||||
|
Query.remove_event_filters(comparison_query, [:goal, :props])
|
||||||
|
|
||||||
%{
|
comparison_without_filters =
|
||||||
visitors: %{value: prev_unique_visitors}
|
site
|
||||||
} = Stats.aggregate(site, totals_comparison_query, [:visitors])
|
|> Stats.aggregate(comparison_query_without_filters, [:visitors])
|
||||||
|
|> transform_keys(%{visitors: :unique_visitors})
|
||||||
|
|
||||||
%{
|
site
|
||||||
visitors: %{value: prev_converted_visitors},
|
|> Stats.aggregate(comparison_query, metrics)
|
||||||
events: %{value: prev_completions}
|
|> transform_keys(%{visitors: :converted_visitors, events: :completions})
|
||||||
} = Stats.aggregate(site, comparison_query, [:visitors, :events])
|
|> Map.merge(comparison_without_filters)
|
||||||
|
end
|
||||||
|
|
||||||
{prev_unique_visitors, prev_converted_visitors, prev_completions}
|
conversion_rate = %{
|
||||||
|
cr: %{value: calculate_cr(results.unique_visitors.value, results.converted_visitors.value)}
|
||||||
|
}
|
||||||
|
|
||||||
|
comparison_conversion_rate =
|
||||||
|
if comparison do
|
||||||
|
value =
|
||||||
|
calculate_cr(comparison.unique_visitors.value, comparison.converted_visitors.value)
|
||||||
|
|
||||||
|
%{cr: %{value: value}}
|
||||||
else
|
else
|
||||||
{nil, nil, nil}
|
nil
|
||||||
end
|
end
|
||||||
|
|
||||||
conversion_rate = calculate_cr(unique_visitors, converted_visitors)
|
[
|
||||||
prev_conversion_rate = calculate_cr(prev_unique_visitors, prev_converted_visitors)
|
top_stats_entry(results, comparison, "Unique visitors", :unique_visitors),
|
||||||
|
top_stats_entry(results, comparison, "Unique conversions", :converted_visitors),
|
||||||
build_item = fn name, value, comparison_value ->
|
top_stats_entry(results, comparison, "Total conversions", :completions),
|
||||||
if comparison_value do
|
top_stats_entry(results, comparison, "Average revenue", :average_revenue, &format_money/1),
|
||||||
change = percent_change(comparison_value, value)
|
top_stats_entry(results, comparison, "Total revenue", :total_revenue, &format_money/1),
|
||||||
%{name: name, value: value, comparison_value: comparison_value, change: change}
|
top_stats_entry(conversion_rate, comparison_conversion_rate, "Conversion rate", :cr)
|
||||||
else
|
]
|
||||||
%{name: name, value: value}
|
|> Enum.reject(&is_nil/1)
|
||||||
end
|
|> then(&{&1, 100})
|
||||||
end
|
|
||||||
|
|
||||||
{[
|
|
||||||
build_item.("Unique visitors", unique_visitors, prev_unique_visitors),
|
|
||||||
build_item.("Unique conversions", converted_visitors, prev_converted_visitors),
|
|
||||||
build_item.("Total conversions", completions, prev_completions),
|
|
||||||
build_item.("Conversion rate", conversion_rate, prev_conversion_rate)
|
|
||||||
], 100}
|
|
||||||
end
|
end
|
||||||
|
|
||||||
defp fetch_top_stats(site, query, comparison_query) do
|
defp fetch_top_stats(site, query, comparison_query) do
|
||||||
@ -420,16 +427,22 @@ defmodule PlausibleWeb.Api.StatsController do
|
|||||||
{stats, current_results[:sample_percent][:value]}
|
{stats, current_results[:sample_percent][:value]}
|
||||||
end
|
end
|
||||||
|
|
||||||
defp top_stats_entry(current_results, prev_results, name, key) do
|
defp top_stats_entry(current_results, prev_results, name, key, formatter \\ & &1) do
|
||||||
if current_results[key] do
|
if current_results[key] do
|
||||||
value = get_in(current_results, [key, :value])
|
value = get_in(current_results, [key, :value])
|
||||||
|
|
||||||
if prev_results do
|
if prev_results do
|
||||||
prev_value = get_in(prev_results, [key, :value])
|
prev_value = get_in(prev_results, [key, :value])
|
||||||
change = calculate_change(key, prev_value, value)
|
change = calculate_change(key, prev_value, value)
|
||||||
%{name: name, value: value, comparison_value: prev_value, change: change}
|
|
||||||
|
%{
|
||||||
|
name: name,
|
||||||
|
value: formatter.(value),
|
||||||
|
comparison_value: formatter.(prev_value),
|
||||||
|
change: change
|
||||||
|
}
|
||||||
else
|
else
|
||||||
%{name: name, value: value}
|
%{name: name, value: formatter.(value)}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@ -444,6 +457,12 @@ defmodule PlausibleWeb.Api.StatsController do
|
|||||||
|
|
||||||
defp percent_change(nil, _new_count), do: nil
|
defp percent_change(nil, _new_count), do: nil
|
||||||
|
|
||||||
|
defp percent_change(%Money{} = old_count, %Money{} = new_count) do
|
||||||
|
old_count = old_count |> Money.to_decimal() |> Decimal.to_float()
|
||||||
|
new_count = new_count |> Money.to_decimal() |> Decimal.to_float()
|
||||||
|
percent_change(old_count, new_count)
|
||||||
|
end
|
||||||
|
|
||||||
defp percent_change(old_count, new_count) do
|
defp percent_change(old_count, new_count) do
|
||||||
cond do
|
cond do
|
||||||
old_count == 0 and new_count > 0 ->
|
old_count == 0 and new_count > 0 ->
|
||||||
@ -1062,7 +1081,8 @@ defmodule PlausibleWeb.Api.StatsController do
|
|||||||
goal
|
goal
|
||||||
|> Map.put(:prop_names, CustomProps.props_for_goal(site, query))
|
|> Map.put(:prop_names, CustomProps.props_for_goal(site, query))
|
||||||
|> Map.put(:conversion_rate, calculate_cr(total_visitors, goal[:unique_conversions]))
|
|> Map.put(:conversion_rate, calculate_cr(total_visitors, goal[:unique_conversions]))
|
||||||
|> format_revenue_metrics()
|
|> Enum.map(&format_revenue_metric/1)
|
||||||
|
|> Map.new()
|
||||||
end)
|
end)
|
||||||
|
|
||||||
if params["csv"] do
|
if params["csv"] do
|
||||||
@ -1072,21 +1092,27 @@ defmodule PlausibleWeb.Api.StatsController do
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
defp format_revenue_metrics(%{average_revenue: %Money{}, total_revenue: %Money{}} = results) do
|
@revenue_metrics [:average_revenue, :total_revenue]
|
||||||
%{
|
defp format_revenue_metric({metric, value}) do
|
||||||
results
|
if metric in @revenue_metrics do
|
||||||
| average_revenue: %{
|
{metric, format_money(value)}
|
||||||
short: Money.to_string!(results.average_revenue, format: :short, fractional_digits: 1),
|
else
|
||||||
long: Money.to_string!(results.average_revenue)
|
{metric, value}
|
||||||
},
|
end
|
||||||
total_revenue: %{
|
|
||||||
short: Money.to_string!(results.total_revenue, format: :short, fractional_digits: 1),
|
|
||||||
long: Money.to_string!(results.total_revenue)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
end
|
end
|
||||||
|
|
||||||
defp format_revenue_metrics(results), do: results
|
defp format_money(value) do
|
||||||
|
case value do
|
||||||
|
%Money{} ->
|
||||||
|
%{
|
||||||
|
short: Money.to_string!(value, format: :short, fractional_digits: 1),
|
||||||
|
long: Money.to_string!(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
_any ->
|
||||||
|
value
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def prop_breakdown(conn, params) do
|
def prop_breakdown(conn, params) do
|
||||||
site = conn.assigns[:site]
|
site = conn.assigns[:site]
|
||||||
@ -1162,13 +1188,12 @@ defmodule PlausibleWeb.Api.StatsController do
|
|||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
defp transform_keys(results, keys_to_replace) do
|
defp transform_keys(result, keys_to_replace) when is_map(result) do
|
||||||
Enum.map(results, fn map ->
|
for {key, val} <- result, do: {Map.get(keys_to_replace, key, key), val}, into: %{}
|
||||||
Enum.map(map, fn {key, val} ->
|
end
|
||||||
{Map.get(keys_to_replace, key, key), val}
|
|
||||||
end)
|
defp transform_keys(results, keys_to_replace) when is_list(results) do
|
||||||
|> Enum.into(%{})
|
Enum.map(results, &transform_keys(&1, keys_to_replace))
|
||||||
end)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
defp parse_pagination(params) do
|
defp parse_pagination(params) do
|
||||||
|
@ -36,6 +36,7 @@ site =
|
|||||||
{:ok, goal1} = Plausible.Goals.create(site, %{"page_path" => "/"})
|
{:ok, goal1} = Plausible.Goals.create(site, %{"page_path" => "/"})
|
||||||
{:ok, goal2} = Plausible.Goals.create(site, %{"page_path" => "/register"})
|
{:ok, goal2} = Plausible.Goals.create(site, %{"page_path" => "/register"})
|
||||||
{:ok, goal3} = Plausible.Goals.create(site, %{"page_path" => "/login"})
|
{:ok, goal3} = Plausible.Goals.create(site, %{"page_path" => "/login"})
|
||||||
|
{:ok, goal4} = Plausible.Goals.create(site, %{"event_name" => "Purchase", "currency" => "USD"})
|
||||||
|
|
||||||
{:ok, _funnel} =
|
{:ok, _funnel} =
|
||||||
Plausible.Funnels.create(site, "From homepage to login", [
|
Plausible.Funnels.create(site, "From homepage to login", [
|
||||||
@ -130,6 +131,35 @@ native_stats_range
|
|||||||
end)
|
end)
|
||||||
|> Plausible.TestUtils.populate_stats()
|
|> Plausible.TestUtils.populate_stats()
|
||||||
|
|
||||||
|
native_stats_range
|
||||||
|
|> Enum.with_index()
|
||||||
|
|> Enum.flat_map(fn {date, index} ->
|
||||||
|
Enum.map(0..Enum.random(1..50), fn _ ->
|
||||||
|
geolocation = Enum.random(geolocations)
|
||||||
|
|
||||||
|
[
|
||||||
|
name: goal4.event_name,
|
||||||
|
site_id: site.id,
|
||||||
|
hostname: site.domain,
|
||||||
|
timestamp: put_random_time.(date, index),
|
||||||
|
referrer_source: Enum.random(["", "Facebook", "Twitter", "DuckDuckGo", "Google"]),
|
||||||
|
browser: Enum.random(["Edge", "Chrome", "Safari", "Firefox", "Vivaldi"]),
|
||||||
|
browser_version: to_string(Enum.random(0..50)),
|
||||||
|
screen_size: Enum.random(["Mobile", "Tablet", "Desktop", "Laptop"]),
|
||||||
|
operating_system: Enum.random(["Windows", "macOS", "Linux"]),
|
||||||
|
operating_system_version: to_string(Enum.random(0..15)),
|
||||||
|
pathname:
|
||||||
|
Enum.random(["/", "/login", "/settings", "/register", "/docs", "/docs/1", "/docs/2"]),
|
||||||
|
user_id: Enum.random(1..1200),
|
||||||
|
revenue_reporting_amount: Decimal.new(Enum.random(100..10000)),
|
||||||
|
revenue_reporting_currency: "USD"
|
||||||
|
]
|
||||||
|
|> Keyword.merge(geolocation)
|
||||||
|
|> then(&Plausible.Factory.build(:event, &1))
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
|> Plausible.TestUtils.populate_stats()
|
||||||
|
|
||||||
site =
|
site =
|
||||||
site
|
site
|
||||||
|> Plausible.Site.start_import(
|
|> Plausible.Site.start_import(
|
||||||
|
@ -690,6 +690,141 @@ defmodule PlausibleWeb.Api.StatsController.TopStatsTest do
|
|||||||
|
|
||||||
assert %{"name" => "Conversion rate", "value" => 33.3} in res["top_stats"]
|
assert %{"name" => "Conversion rate", "value" => 33.3} in res["top_stats"]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "returns average and total when filtering by a revenue goal", %{conn: conn, site: site} do
|
||||||
|
insert(:goal, site: site, event_name: "Payment", currency: "USD")
|
||||||
|
insert(:goal, site: site, event_name: "AddToCart", currency: "EUR")
|
||||||
|
|
||||||
|
populate_stats(site, [
|
||||||
|
build(:event,
|
||||||
|
name: "Payment",
|
||||||
|
revenue_reporting_amount: Decimal.new(13_29),
|
||||||
|
revenue_reporting_currency: "USD"
|
||||||
|
),
|
||||||
|
build(:event,
|
||||||
|
name: "Payment",
|
||||||
|
revenue_reporting_amount: Decimal.new(19_90),
|
||||||
|
revenue_reporting_currency: "USD"
|
||||||
|
),
|
||||||
|
build(:event,
|
||||||
|
name: "AddToCart",
|
||||||
|
revenue_reporting_amount: Decimal.new(10_31),
|
||||||
|
revenue_reporting_currency: "EUR"
|
||||||
|
),
|
||||||
|
build(:event,
|
||||||
|
name: "AddToCart",
|
||||||
|
revenue_reporting_amount: Decimal.new(20_00),
|
||||||
|
revenue_reporting_currency: "EUR"
|
||||||
|
)
|
||||||
|
])
|
||||||
|
|
||||||
|
filters = Jason.encode!(%{goal: "Payment"})
|
||||||
|
conn = get(conn, "/api/stats/#{site.domain}/top-stats?period=all&filters=#{filters}")
|
||||||
|
assert %{"top_stats" => top_stats} = json_response(conn, 200)
|
||||||
|
|
||||||
|
assert %{
|
||||||
|
"name" => "Average revenue",
|
||||||
|
"value" => %{"long" => "$1,659.50", "short" => "$1.7K"}
|
||||||
|
} in top_stats
|
||||||
|
|
||||||
|
assert %{
|
||||||
|
"name" => "Total revenue",
|
||||||
|
"value" => %{"long" => "$3,319.00", "short" => "$3.3K"}
|
||||||
|
} in top_stats
|
||||||
|
end
|
||||||
|
|
||||||
|
test "returns average and total when filtering by many revenue goals with same currency", %{
|
||||||
|
conn: conn,
|
||||||
|
site: site
|
||||||
|
} do
|
||||||
|
insert(:goal, site: site, event_name: "Payment", currency: "USD")
|
||||||
|
insert(:goal, site: site, event_name: "Payment2", currency: "USD")
|
||||||
|
insert(:goal, site: site, event_name: "AddToCart", currency: "EUR")
|
||||||
|
|
||||||
|
populate_stats(site, [
|
||||||
|
build(:event,
|
||||||
|
name: "Payment",
|
||||||
|
revenue_reporting_amount: Decimal.new(13_29),
|
||||||
|
revenue_reporting_currency: "USD"
|
||||||
|
),
|
||||||
|
build(:event,
|
||||||
|
name: "Payment",
|
||||||
|
revenue_reporting_amount: Decimal.new(19_90),
|
||||||
|
revenue_reporting_currency: "USD"
|
||||||
|
),
|
||||||
|
build(:event,
|
||||||
|
name: "Payment2",
|
||||||
|
revenue_reporting_amount: Decimal.new(13_29),
|
||||||
|
revenue_reporting_currency: "USD"
|
||||||
|
),
|
||||||
|
build(:event,
|
||||||
|
name: "Payment2",
|
||||||
|
revenue_reporting_amount: Decimal.new(19_90),
|
||||||
|
revenue_reporting_currency: "USD"
|
||||||
|
),
|
||||||
|
build(:event,
|
||||||
|
name: "AddToCart",
|
||||||
|
revenue_reporting_amount: Decimal.new(10_31),
|
||||||
|
revenue_reporting_currency: "EUR"
|
||||||
|
),
|
||||||
|
build(:event,
|
||||||
|
name: "AddToCart",
|
||||||
|
revenue_reporting_amount: Decimal.new(20_00),
|
||||||
|
revenue_reporting_currency: "EUR"
|
||||||
|
)
|
||||||
|
])
|
||||||
|
|
||||||
|
filters = Jason.encode!(%{goal: "Payment|Payment2"})
|
||||||
|
conn = get(conn, "/api/stats/#{site.domain}/top-stats?period=all&filters=#{filters}")
|
||||||
|
assert %{"top_stats" => top_stats} = json_response(conn, 200)
|
||||||
|
|
||||||
|
assert %{
|
||||||
|
"name" => "Average revenue",
|
||||||
|
"value" => %{"long" => "$1,659.50", "short" => "$1.7K"}
|
||||||
|
} in top_stats
|
||||||
|
|
||||||
|
assert %{
|
||||||
|
"name" => "Total revenue",
|
||||||
|
"value" => %{"long" => "$6,638.00", "short" => "$6.6K"}
|
||||||
|
} in top_stats
|
||||||
|
end
|
||||||
|
|
||||||
|
test "does not return average and total when filtering by many revenue goals with different currencies",
|
||||||
|
%{conn: conn, site: site} do
|
||||||
|
insert(:goal, site: site, event_name: "Payment", currency: "USD")
|
||||||
|
insert(:goal, site: site, event_name: "AddToCart", currency: "EUR")
|
||||||
|
|
||||||
|
populate_stats(site, [
|
||||||
|
build(:event,
|
||||||
|
name: "Payment",
|
||||||
|
revenue_reporting_amount: Decimal.new(13_29),
|
||||||
|
revenue_reporting_currency: "USD"
|
||||||
|
),
|
||||||
|
build(:event,
|
||||||
|
name: "Payment",
|
||||||
|
revenue_reporting_amount: Decimal.new(19_90),
|
||||||
|
revenue_reporting_currency: "USD"
|
||||||
|
),
|
||||||
|
build(:event,
|
||||||
|
name: "AddToCart",
|
||||||
|
revenue_reporting_amount: Decimal.new(10_31),
|
||||||
|
revenue_reporting_currency: "EUR"
|
||||||
|
),
|
||||||
|
build(:event,
|
||||||
|
name: "AddToCart",
|
||||||
|
revenue_reporting_amount: Decimal.new(20_00),
|
||||||
|
revenue_reporting_currency: "EUR"
|
||||||
|
)
|
||||||
|
])
|
||||||
|
|
||||||
|
filters = Jason.encode!(%{goal: "Payment|AddToCart"})
|
||||||
|
conn = get(conn, "/api/stats/#{site.domain}/top-stats?period=all&filters=#{filters}")
|
||||||
|
assert %{"top_stats" => top_stats} = json_response(conn, 200)
|
||||||
|
|
||||||
|
metrics = Enum.map(top_stats, & &1["name"])
|
||||||
|
refute "Average revenue" in metrics
|
||||||
|
refute "Total revenue" in metrics
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "GET /api/stats/top-stats - with comparisons" do
|
describe "GET /api/stats/top-stats - with comparisons" do
|
||||||
|
Loading…
Reference in New Issue
Block a user