mirror of
https://github.com/plausible/analytics.git
synced 2024-12-28 12:01:39 +03:00
f576fa2a2c
* Add details=True to export API parameters This makes the ZIP export add `%{"details" => "True"}` to the query's `params` when fetching data internally for packaging in the ZIP. This adds bounce_rate and time_on_page to the data in pages.csv, and bounce_rate and visit_duration to sources.csv. * Make API return data with consistent names Some of the data types returned via the JSON or CSV API use inconsistent naming, and some have redundant name changes (i.e. count -> visitors -> count). This makes these all consistent and removes the redundancy. This addresses #1426, fixes some of the CSV headers, and unifies the JSON and CSV return data labels. * Update changelog * Test should use Timex.shift, not relative time * Return full country names in CSV export This also replaces the " character with ' in two country names, as those are the characters used in the names, yielding a more predictable and 'correct' output. * Fetch CSV exported data concurrently * Use spinner to indicate when export has started * Use 300 as default number of brekadown entries for export Higher numbers (e.g. 1000) seem to cause clickhouse errors when there many pages to request. It is unclear what is causing the error, as clickhouse returns an "unknown" error code and an empty error message.
552 lines
15 KiB
Elixir
552 lines
15 KiB
Elixir
defmodule PlausibleWeb.Api.StatsController.SourcesTest do
|
|
use PlausibleWeb.ConnCase
|
|
import Plausible.TestUtils
|
|
@user_id 123
|
|
|
|
describe "GET /api/stats/:domain/sources" do
|
|
setup [:create_user, :log_in, :create_new_site]
|
|
|
|
test "returns top sources by unique user ids", %{conn: conn, site: site} do
|
|
populate_stats(site, [
|
|
build(:pageview,
|
|
referrer_source: "Google",
|
|
referrer: "google.com"
|
|
),
|
|
build(:pageview,
|
|
referrer_source: "Google",
|
|
referrer: "google.com"
|
|
),
|
|
build(:pageview,
|
|
referrer_source: "DuckDuckGo",
|
|
referrer: "duckduckgo.com"
|
|
)
|
|
])
|
|
|
|
conn = get(conn, "/api/stats/#{site.domain}/sources")
|
|
|
|
assert json_response(conn, 200) == [
|
|
%{"name" => "Google", "visitors" => 2},
|
|
%{"name" => "DuckDuckGo", "visitors" => 1}
|
|
]
|
|
end
|
|
|
|
test "calculates bounce rate and visit duration for sources", %{conn: conn, site: site} do
|
|
populate_stats(site, [
|
|
build(:pageview,
|
|
referrer_source: "Google",
|
|
referrer: "google.com",
|
|
user_id: @user_id,
|
|
timestamp: ~N[2021-01-01 00:00:00]
|
|
),
|
|
build(:pageview,
|
|
referrer_source: "Google",
|
|
referrer: "google.com",
|
|
user_id: @user_id,
|
|
timestamp: ~N[2021-01-01 00:15:00]
|
|
),
|
|
build(:pageview,
|
|
referrer_source: "DuckDuckGo",
|
|
referrer: "duckduckgo.com",
|
|
timestamp: ~N[2021-01-01 00:00:00]
|
|
)
|
|
])
|
|
|
|
conn =
|
|
get(
|
|
conn,
|
|
"/api/stats/#{site.domain}/sources?period=day&date=2021-01-01&detailed=true"
|
|
)
|
|
|
|
assert json_response(conn, 200) == [
|
|
%{
|
|
"name" => "Google",
|
|
"visitors" => 1,
|
|
"bounce_rate" => 0,
|
|
"visit_duration" => 900
|
|
},
|
|
%{
|
|
"name" => "DuckDuckGo",
|
|
"visitors" => 1,
|
|
"bounce_rate" => 100,
|
|
"visit_duration" => 0
|
|
}
|
|
]
|
|
end
|
|
|
|
test "returns top sources in realtime report", %{conn: conn, site: site} do
|
|
populate_stats(site, [
|
|
build(:pageview,
|
|
referrer_source: "Google",
|
|
referrer: "google.com",
|
|
timestamp: relative_time(minutes: -3)
|
|
),
|
|
build(:pageview,
|
|
referrer_source: "Google",
|
|
referrer: "google.com",
|
|
timestamp: relative_time(minutes: -2)
|
|
),
|
|
build(:pageview,
|
|
referrer_source: "DuckDuckGo",
|
|
referrer: "duckduckgo.com",
|
|
timestamp: relative_time(minutes: -1)
|
|
)
|
|
])
|
|
|
|
conn = get(conn, "/api/stats/#{site.domain}/sources?period=realtime")
|
|
|
|
assert json_response(conn, 200) == [
|
|
%{"name" => "Google", "visitors" => 2},
|
|
%{"name" => "DuckDuckGo", "visitors" => 1}
|
|
]
|
|
end
|
|
|
|
test "can paginate the results", %{conn: conn, site: site} do
|
|
populate_stats(site, [
|
|
build(:pageview,
|
|
referrer_source: "Google",
|
|
referrer: "google.com"
|
|
),
|
|
build(:pageview,
|
|
referrer_source: "Google",
|
|
referrer: "google.com"
|
|
),
|
|
build(:pageview,
|
|
referrer_source: "DuckDuckGo",
|
|
referrer: "duckduckgo.com"
|
|
)
|
|
])
|
|
|
|
conn = get(conn, "/api/stats/#{site.domain}/sources?limit=1&page=2")
|
|
|
|
assert json_response(conn, 200) == [
|
|
%{"name" => "DuckDuckGo", "visitors" => 1}
|
|
]
|
|
end
|
|
|
|
test "shows sources for a page", %{conn: conn, site: site} do
|
|
populate_stats(site, [
|
|
build(:pageview, pathname: "/page1", referrer_source: "Google"),
|
|
build(:pageview, pathname: "/page2", referrer_source: "Google"),
|
|
build(:pageview, user_id: 1, pathname: "/page2", referrer_source: "DuckDuckGo"),
|
|
build(:pageview, user_id: 1, pathname: "/page1", referrer_source: "DuckDuckGo")
|
|
])
|
|
|
|
filters = Jason.encode!(%{"page" => "/page1"})
|
|
conn = get(conn, "/api/stats/#{site.domain}/sources?filters=#{filters}")
|
|
|
|
assert json_response(conn, 200) == [
|
|
%{"name" => "Google", "visitors" => 1}
|
|
]
|
|
end
|
|
end
|
|
|
|
describe "GET /api/stats/:domain/utm_mediums" do
|
|
setup [:create_user, :log_in, :create_new_site]
|
|
|
|
test "returns top utm_mediums by unique user ids", %{conn: conn, site: site} do
|
|
populate_stats(site, [
|
|
build(:pageview,
|
|
utm_medium: "social",
|
|
user_id: @user_id,
|
|
timestamp: ~N[2021-01-01 00:00:00]
|
|
),
|
|
build(:pageview,
|
|
utm_medium: "social",
|
|
user_id: @user_id,
|
|
timestamp: ~N[2021-01-01 00:15:00]
|
|
),
|
|
build(:pageview,
|
|
utm_medium: "email",
|
|
timestamp: ~N[2021-01-01 00:00:00]
|
|
)
|
|
])
|
|
|
|
conn =
|
|
get(
|
|
conn,
|
|
"/api/stats/#{site.domain}/utm_mediums?period=day&date=2021-01-01"
|
|
)
|
|
|
|
assert json_response(conn, 200) == [
|
|
%{
|
|
"name" => "social",
|
|
"visitors" => 1,
|
|
"bounce_rate" => 0,
|
|
"visit_duration" => 900
|
|
},
|
|
%{
|
|
"name" => "email",
|
|
"visitors" => 1,
|
|
"bounce_rate" => 100,
|
|
"visit_duration" => 0
|
|
}
|
|
]
|
|
end
|
|
end
|
|
|
|
describe "GET /api/stats/:domain/utm_campaigns" do
|
|
setup [:create_user, :log_in, :create_new_site]
|
|
|
|
test "returns top utm_campaigns by unique user ids", %{conn: conn, site: site} do
|
|
populate_stats(site, [
|
|
build(:pageview,
|
|
utm_campaign: "profile",
|
|
user_id: @user_id,
|
|
timestamp: ~N[2021-01-01 00:00:00]
|
|
),
|
|
build(:pageview,
|
|
utm_campaign: "profile",
|
|
user_id: @user_id,
|
|
timestamp: ~N[2021-01-01 00:15:00]
|
|
),
|
|
build(:pageview,
|
|
utm_campaign: "august",
|
|
timestamp: ~N[2021-01-01 00:00:00]
|
|
),
|
|
build(:pageview,
|
|
utm_campaign: "august",
|
|
timestamp: ~N[2021-01-01 00:00:00]
|
|
)
|
|
])
|
|
|
|
conn =
|
|
get(
|
|
conn,
|
|
"/api/stats/#{site.domain}/utm_campaigns?period=day&date=2021-01-01"
|
|
)
|
|
|
|
assert json_response(conn, 200) == [
|
|
%{
|
|
"name" => "august",
|
|
"visitors" => 2,
|
|
"bounce_rate" => 100,
|
|
"visit_duration" => 0
|
|
},
|
|
%{
|
|
"name" => "profile",
|
|
"visitors" => 1,
|
|
"bounce_rate" => 0,
|
|
"visit_duration" => 900
|
|
}
|
|
]
|
|
end
|
|
end
|
|
|
|
describe "GET /api/stats/:domain/utm_sources" do
|
|
setup [:create_user, :log_in, :create_new_site]
|
|
|
|
test "returns top utm_sources by unique user ids", %{conn: conn, site: site} do
|
|
populate_stats(site, [
|
|
build(:pageview,
|
|
utm_source: "Twitter",
|
|
user_id: @user_id,
|
|
timestamp: ~N[2021-01-01 00:00:00]
|
|
),
|
|
build(:pageview,
|
|
utm_source: "Twitter",
|
|
user_id: @user_id,
|
|
timestamp: ~N[2021-01-01 00:15:00]
|
|
),
|
|
build(:pageview,
|
|
utm_source: "newsletter",
|
|
timestamp: ~N[2021-01-01 00:00:00]
|
|
),
|
|
build(:pageview,
|
|
utm_source: "newsletter",
|
|
timestamp: ~N[2021-01-01 00:00:00]
|
|
)
|
|
])
|
|
|
|
conn =
|
|
get(
|
|
conn,
|
|
"/api/stats/#{site.domain}/utm_sources?period=day&date=2021-01-01"
|
|
)
|
|
|
|
assert json_response(conn, 200) == [
|
|
%{
|
|
"name" => "newsletter",
|
|
"visitors" => 2,
|
|
"bounce_rate" => 100,
|
|
"visit_duration" => 0
|
|
},
|
|
%{
|
|
"name" => "Twitter",
|
|
"visitors" => 1,
|
|
"bounce_rate" => 0,
|
|
"visit_duration" => 900
|
|
}
|
|
]
|
|
end
|
|
end
|
|
|
|
describe "GET /api/stats/:domain/sources - with goal filter" do
|
|
setup [:create_user, :log_in, :create_new_site]
|
|
|
|
test "returns top referrers for a custom goal including conversion_rate", %{
|
|
conn: conn,
|
|
site: site
|
|
} do
|
|
populate_stats(site, [
|
|
build(:pageview,
|
|
referrer_source: "Twitter",
|
|
user_id: @user_id
|
|
),
|
|
build(:event,
|
|
name: "Signup",
|
|
user_id: @user_id
|
|
),
|
|
build(:pageview,
|
|
referrer_source: "Twitter"
|
|
)
|
|
])
|
|
|
|
filters = Jason.encode!(%{goal: "Signup"})
|
|
|
|
conn =
|
|
get(
|
|
conn,
|
|
"/api/stats/#{site.domain}/sources?period=day&filters=#{filters}"
|
|
)
|
|
|
|
assert json_response(conn, 200) == [
|
|
%{
|
|
"name" => "Twitter",
|
|
"total_visitors" => 2,
|
|
"visitors" => 1,
|
|
"conversion_rate" => 50.0
|
|
}
|
|
]
|
|
end
|
|
|
|
test "returns top referrers for a pageview goal including conversion_rate", %{
|
|
conn: conn,
|
|
site: site
|
|
} do
|
|
populate_stats(site, [
|
|
build(:pageview,
|
|
referrer_source: "Twitter",
|
|
user_id: @user_id
|
|
),
|
|
build(:pageview,
|
|
pathname: "/register",
|
|
user_id: @user_id
|
|
),
|
|
build(:pageview,
|
|
referrer_source: "Twitter"
|
|
)
|
|
])
|
|
|
|
filters = Jason.encode!(%{goal: "Visit /register"})
|
|
|
|
conn =
|
|
get(
|
|
conn,
|
|
"/api/stats/#{site.domain}/sources?period=day&filters=#{filters}"
|
|
)
|
|
|
|
assert json_response(conn, 200) == [
|
|
%{
|
|
"name" => "Twitter",
|
|
"total_visitors" => 2,
|
|
"visitors" => 1,
|
|
"conversion_rate" => 50.0
|
|
}
|
|
]
|
|
end
|
|
end
|
|
|
|
describe "GET /api/stats/:domain/referrer-drilldown" do
|
|
setup [:create_user, :log_in, :create_new_site]
|
|
|
|
test "returns top referrers for a particular source", %{conn: conn, site: site} do
|
|
populate_stats(site, [
|
|
build(:pageview,
|
|
referrer_source: "10words",
|
|
referrer: "10words.com"
|
|
),
|
|
build(:pageview,
|
|
referrer_source: "10words",
|
|
referrer: "10words.com"
|
|
),
|
|
build(:pageview,
|
|
referrer_source: "10words",
|
|
referrer: "10words.com/page1"
|
|
),
|
|
build(:pageview,
|
|
referrer_source: "ignored",
|
|
referrer: "ignored"
|
|
)
|
|
])
|
|
|
|
conn =
|
|
get(
|
|
conn,
|
|
"/api/stats/#{site.domain}/referrers/10words?period=day"
|
|
)
|
|
|
|
assert json_response(conn, 200) == %{
|
|
"total_visitors" => 3,
|
|
"referrers" => [
|
|
%{"name" => "10words.com", "visitors" => 2},
|
|
%{"name" => "10words.com/page1", "visitors" => 1}
|
|
]
|
|
}
|
|
end
|
|
|
|
test "calculates bounce rate and visit duration for referrer urls", %{conn: conn, site: site} do
|
|
populate_stats(site, [
|
|
build(:pageview,
|
|
referrer_source: "10words",
|
|
referrer: "10words.com",
|
|
user_id: @user_id,
|
|
timestamp: ~N[2021-01-01 00:00:00]
|
|
),
|
|
build(:pageview,
|
|
referrer_source: "10words",
|
|
referrer: "10words.com",
|
|
user_id: @user_id,
|
|
timestamp: ~N[2021-01-01 00:15:00]
|
|
),
|
|
build(:pageview,
|
|
referrer_source: "10words",
|
|
referrer: "10words.com",
|
|
timestamp: ~N[2021-01-01 00:15:00]
|
|
),
|
|
build(:pageview,
|
|
referrer_source: "ignored",
|
|
referrer: "ignored",
|
|
timestamp: ~N[2021-01-01 00:00:00]
|
|
)
|
|
])
|
|
|
|
conn =
|
|
get(
|
|
conn,
|
|
"/api/stats/#{site.domain}/referrers/10words?period=day&date=2021-01-01&detailed=true"
|
|
)
|
|
|
|
assert json_response(conn, 200) == %{
|
|
"total_visitors" => 2,
|
|
"referrers" => [
|
|
%{
|
|
"name" => "10words.com",
|
|
"visitors" => 2,
|
|
"bounce_rate" => 50.0,
|
|
"visit_duration" => 450
|
|
}
|
|
]
|
|
}
|
|
end
|
|
|
|
test "gets keywords from Google", %{conn: conn, user: user, site: site} do
|
|
insert(:google_auth, user: user, user: user, site: site, property: "sc-domain:example.com")
|
|
|
|
populate_stats(site, [
|
|
build(:pageview,
|
|
referrer_source: "DuckDuckGo",
|
|
referrer: "duckduckgo.com"
|
|
),
|
|
build(:pageview,
|
|
referrer_source: "Google",
|
|
referrer: "google.com"
|
|
),
|
|
build(:pageview,
|
|
referrer_source: "Google",
|
|
referrer: "google.com"
|
|
)
|
|
])
|
|
|
|
conn = get(conn, "/api/stats/#{site.domain}/referrers/Google?period=day")
|
|
{:ok, terms} = Plausible.Google.Api.Mock.fetch_stats(nil, nil, nil)
|
|
|
|
assert json_response(conn, 200) == %{
|
|
"total_visitors" => 2,
|
|
"search_terms" => terms
|
|
}
|
|
end
|
|
|
|
test "returns top referring urls for a custom goal", %{conn: conn, site: site} do
|
|
populate_stats(site, [
|
|
build(:pageview,
|
|
referrer_source: "10words",
|
|
referrer: "10words.com"
|
|
),
|
|
build(:pageview,
|
|
referrer_source: "10words",
|
|
referrer: "10words.com",
|
|
user_id: @user_id
|
|
),
|
|
build(:event,
|
|
name: "Signup",
|
|
user_id: @user_id
|
|
),
|
|
build(:event,
|
|
name: "Signup"
|
|
)
|
|
])
|
|
|
|
filters = Jason.encode!(%{goal: "Signup"})
|
|
|
|
conn =
|
|
get(
|
|
conn,
|
|
"/api/stats/#{site.domain}/referrers/10words?period=day&filters=#{filters}"
|
|
)
|
|
|
|
assert json_response(conn, 200) == %{
|
|
"total_visitors" => 1,
|
|
"referrers" => [
|
|
%{
|
|
"name" => "10words.com",
|
|
"total_visitors" => 2,
|
|
"conversion_rate" => 50.0,
|
|
"visitors" => 1
|
|
}
|
|
]
|
|
}
|
|
end
|
|
|
|
test "returns top referring urls for a pageview goal", %{conn: conn, site: site} do
|
|
populate_stats(site, [
|
|
build(:pageview,
|
|
referrer_source: "10words",
|
|
referrer: "10words.com"
|
|
),
|
|
build(:pageview,
|
|
referrer_source: "10words",
|
|
referrer: "10words.com",
|
|
user_id: @user_id
|
|
),
|
|
build(:pageview,
|
|
pathname: "/register",
|
|
user_id: @user_id
|
|
),
|
|
build(:pageview,
|
|
pathname: "/register"
|
|
)
|
|
])
|
|
|
|
filters = Jason.encode!(%{goal: "Visit /register"})
|
|
|
|
conn =
|
|
get(
|
|
conn,
|
|
"/api/stats/#{site.domain}/referrers/10words?period=day&filters=#{filters}"
|
|
)
|
|
|
|
assert json_response(conn, 200) == %{
|
|
"total_visitors" => 1,
|
|
"referrers" => [
|
|
%{
|
|
"name" => "10words.com",
|
|
"total_visitors" => 2,
|
|
"conversion_rate" => 50.0,
|
|
"visitors" => 1
|
|
}
|
|
]
|
|
}
|
|
end
|
|
end
|
|
end
|