mirror of
https://github.com/plausible/analytics.git
synced 2024-12-23 01:22:15 +03:00
Analytics without using cookies (#44)
* Use fingerprints instead of user_id * Fix tests
This commit is contained in:
parent
6d1f7afde5
commit
594b085467
@ -4,9 +4,11 @@
|
||||
try {
|
||||
const scriptEl = window.document.querySelector('[src*="' + plausibleHost +'"]')
|
||||
const domainAttr = scriptEl && scriptEl.getAttribute('data-domain')
|
||||
const trackAcquisitionAttr = scriptEl && scriptEl.getAttribute('data-track-acquisition')
|
||||
|
||||
const CONFIG = {
|
||||
domain: domainAttr || window.location.hostname
|
||||
domain: domainAttr || window.location.hostname,
|
||||
trackAcquisition: typeof(trackAcquisitionAttr) === 'string'
|
||||
}
|
||||
|
||||
function setCookie(name,value) {
|
||||
@ -23,18 +25,6 @@
|
||||
return matches ? decodeURIComponent(matches[1]) : null;
|
||||
}
|
||||
|
||||
function pseudoUUIDv4() {
|
||||
var d = new Date().getTime();
|
||||
if (typeof performance !== 'undefined' && typeof performance.now === 'function'){
|
||||
d += performance.now(); //use high-precision timer if available
|
||||
}
|
||||
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
|
||||
var r = (d + Math.random() * 16) % 16 | 0;
|
||||
d = Math.floor(d / 16);
|
||||
return (c === 'x' ? r : (r & 0x3 | 0x8)).toString(16);
|
||||
});
|
||||
}
|
||||
|
||||
function ignore(reason) {
|
||||
console.warn('[Plausible] Ignoring event because ' + reason);
|
||||
}
|
||||
@ -52,30 +42,23 @@
|
||||
var userData = JSON.parse(getCookie('plausible_user'))
|
||||
|
||||
if (userData) {
|
||||
userData.new_visitor = false
|
||||
if (userData.referrer) {
|
||||
userData.initial_referrer = userData.referrer && decodeURIComponent(userData.referrer)
|
||||
} else {
|
||||
userData.initial_referrer = userData.initial_referrer && decodeURIComponent(userData.initial_referrer)
|
||||
userData.initial_source = userData.initial_source && decodeURIComponent(userData.initial_source)
|
||||
}
|
||||
return userData
|
||||
} else {
|
||||
return {
|
||||
uid: pseudoUUIDv4(),
|
||||
new_visitor: true,
|
||||
initial_referrer: window.document.referrer,
|
||||
initial_referrer: userData.initial_referrer && decodeURIComponent(userData.initial_referrer),
|
||||
initial_source: userData.initial_source && decodeURIComponent(userData.initial_source)
|
||||
}
|
||||
} else {
|
||||
userData = {
|
||||
initial_referrer: window.document.referrer || null,
|
||||
initial_source: getSourceFromQueryParam(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function setUserData(payload) {
|
||||
setCookie('plausible_user', JSON.stringify({
|
||||
uid: payload.uid,
|
||||
initial_referrer: payload.initial_referrer && encodeURIComponent(payload.initial_referrer),
|
||||
initial_source: payload.initial_source && encodeURIComponent(payload.initial_source),
|
||||
}))
|
||||
setCookie('plausible_user', JSON.stringify({
|
||||
initial_referrer: userData.initial_referrer && encodeURIComponent(userData.initial_referrer),
|
||||
initial_source: userData.initial_source && encodeURIComponent(userData.initial_source),
|
||||
}))
|
||||
|
||||
return userData
|
||||
}
|
||||
}
|
||||
|
||||
function trigger(eventName, options) {
|
||||
@ -83,11 +66,11 @@
|
||||
if (window.location.protocol === 'file:') return ignore('website is running locally');
|
||||
if (window.document.visibilityState === 'prerender') return ignore('document is prerendering');
|
||||
|
||||
var payload = getUserData()
|
||||
var payload = CONFIG['trackAcquisition'] ? getUserData() : {}
|
||||
payload.name = eventName
|
||||
payload.url = getUrl()
|
||||
payload.domain = CONFIG['domain']
|
||||
payload.referrer = window.document.referrer
|
||||
payload.referrer = window.document.referrer || null
|
||||
payload.source = getSourceFromQueryParam()
|
||||
payload.user_agent = window.navigator.userAgent
|
||||
payload.screen_width = window.innerWidth
|
||||
@ -100,7 +83,6 @@
|
||||
|
||||
request.onreadystatechange = function() {
|
||||
if (request.readyState == XMLHttpRequest.DONE) {
|
||||
setUserData(payload)
|
||||
options && options.callback && options.callback()
|
||||
}
|
||||
}
|
||||
|
@ -32,7 +32,7 @@ defmodule Plausible.Stats do
|
||||
from e in base_query(site, %{query | filters: %{}}),
|
||||
group_by: 1,
|
||||
order_by: 1,
|
||||
select: {fragment("date_trunc('month', ? at time zone 'utc' at time zone ?)", e.timestamp, ^site.timezone), count(e.user_id, :distinct)}
|
||||
select: {fragment("date_trunc('month', ? at time zone 'utc' at time zone ?)", e.timestamp, ^site.timezone), count(e.fingerprint, :distinct)}
|
||||
) |> Enum.into(%{})
|
||||
|> transform_keys(fn dt -> NaiveDateTime.to_date(dt) end)
|
||||
|
||||
@ -41,7 +41,7 @@ defmodule Plausible.Stats do
|
||||
from e in base_query(site, query),
|
||||
group_by: 1,
|
||||
order_by: 1,
|
||||
select: {fragment("date_trunc('month', ? at time zone 'utc' at time zone ?)", e.timestamp, ^site.timezone), count(e.user_id, :distinct)}
|
||||
select: {fragment("date_trunc('month', ? at time zone 'utc' at time zone ?)", e.timestamp, ^site.timezone), count(e.fingerprint, :distinct)}
|
||||
) |> Enum.into(%{})
|
||||
|> transform_keys(fn dt -> NaiveDateTime.to_date(dt) end)
|
||||
end
|
||||
@ -61,7 +61,7 @@ defmodule Plausible.Stats do
|
||||
from e in base_query(site, %{ query | filters: %{} }),
|
||||
group_by: 1,
|
||||
order_by: 1,
|
||||
select: {fragment("date_trunc('day', ? at time zone 'utc' at time zone ?)", e.timestamp, ^site.timezone), count(e.user_id, :distinct)}
|
||||
select: {fragment("date_trunc('day', ? at time zone 'utc' at time zone ?)", e.timestamp, ^site.timezone), count(e.fingerprint, :distinct)}
|
||||
) |> Enum.into(%{})
|
||||
|> transform_keys(fn dt -> NaiveDateTime.to_date(dt) end)
|
||||
|
||||
@ -70,7 +70,7 @@ defmodule Plausible.Stats do
|
||||
from e in base_query(site, query),
|
||||
group_by: 1,
|
||||
order_by: 1,
|
||||
select: {fragment("date_trunc('day', ? at time zone 'utc' at time zone ?)", e.timestamp, ^site.timezone), count(e.user_id, :distinct)}
|
||||
select: {fragment("date_trunc('day', ? at time zone 'utc' at time zone ?)", e.timestamp, ^site.timezone), count(e.fingerprint, :distinct)}
|
||||
) |> Enum.into(%{})
|
||||
|> transform_keys(fn dt -> NaiveDateTime.to_date(dt) end)
|
||||
end
|
||||
@ -98,7 +98,7 @@ defmodule Plausible.Stats do
|
||||
from e in base_query(site, %{query | filters: %{}}),
|
||||
group_by: 1,
|
||||
order_by: 1,
|
||||
select: {fragment("date_trunc('hour', ? at time zone 'utc' at time zone ?)", e.timestamp, ^site.timezone), count(e.user_id, :distinct)}
|
||||
select: {fragment("date_trunc('hour', ? at time zone 'utc' at time zone ?)", e.timestamp, ^site.timezone), count(e.fingerprint, :distinct)}
|
||||
)
|
||||
|> Enum.into(%{})
|
||||
|> transform_keys(fn dt -> NaiveDateTime.truncate(dt, :second) end)
|
||||
@ -108,7 +108,7 @@ defmodule Plausible.Stats do
|
||||
from e in base_query(site, query),
|
||||
group_by: 1,
|
||||
order_by: 1,
|
||||
select: {fragment("date_trunc('hour', ? at time zone 'utc' at time zone ?)", e.timestamp, ^site.timezone), count(e.user_id, :distinct)}
|
||||
select: {fragment("date_trunc('hour', ? at time zone 'utc' at time zone ?)", e.timestamp, ^site.timezone), count(e.fingerprint, :distinct)}
|
||||
)
|
||||
|> Enum.into(%{})
|
||||
|> transform_keys(fn dt -> NaiveDateTime.truncate(dt, :second) end)
|
||||
@ -125,7 +125,7 @@ defmodule Plausible.Stats do
|
||||
def bounce_rate(site, query) do
|
||||
{first_datetime, last_datetime} = date_range_utc_boundaries(query.date_range, site.timezone)
|
||||
|
||||
sessions_query = from(s in Plausible.Session,
|
||||
sessions_query = from(s in Plausible.FingerprintSession,
|
||||
where: s.domain == ^site.domain,
|
||||
where: s.start >= ^first_datetime and s.start < ^last_datetime
|
||||
)
|
||||
@ -141,20 +141,20 @@ defmodule Plausible.Stats do
|
||||
def pageviews_and_visitors(site, query) do
|
||||
Repo.one(from(
|
||||
e in base_query(site, query),
|
||||
select: {count(e.id), count(e.user_id, :distinct)}
|
||||
select: {count(e.id), count(e.fingerprint, :distinct)}
|
||||
))
|
||||
end
|
||||
|
||||
def unique_visitors(site, query) do
|
||||
Repo.one(from(
|
||||
e in base_query(site, query),
|
||||
select: count(e.user_id, :distinct)
|
||||
select: count(e.fingerprint, :distinct)
|
||||
))
|
||||
end
|
||||
|
||||
def top_referrers_for_goal(site, query, limit \\ 5) do
|
||||
Repo.all(from e in base_query(site, query),
|
||||
select: %{name: e.initial_referrer_source, url: min(e.initial_referrer), count: count(e.user_id, :distinct)},
|
||||
select: %{name: e.initial_referrer_source, url: min(e.initial_referrer), count: count(e.fingerprint, :distinct)},
|
||||
group_by: e.initial_referrer_source,
|
||||
where: not is_nil(e.initial_referrer_source),
|
||||
order_by: [desc: 3],
|
||||
@ -166,7 +166,7 @@ defmodule Plausible.Stats do
|
||||
|
||||
def top_referrers(site, query, limit \\ 5, include \\ []) do
|
||||
referrers = Repo.all(from e in base_query(site, query),
|
||||
select: %{name: e.referrer_source, url: min(e.referrer), count: count(e.user_id, :distinct)},
|
||||
select: %{name: e.referrer_source, url: min(e.referrer), count: count(e.fingerprint, :distinct)},
|
||||
group_by: e.referrer_source,
|
||||
where: not is_nil(e.referrer_source),
|
||||
order_by: [desc: 3],
|
||||
@ -190,7 +190,7 @@ defmodule Plausible.Stats do
|
||||
{first_datetime, last_datetime} = date_range_utc_boundaries(query.date_range, site.timezone)
|
||||
|
||||
total_sessions_by_referrer = Repo.all(
|
||||
from s in Plausible.Session,
|
||||
from s in Plausible.FingerprintSession,
|
||||
where: s.domain == ^site.domain,
|
||||
where: s.start >= ^first_datetime and s.start < ^last_datetime,
|
||||
where: s.referrer_source in ^referrers,
|
||||
@ -199,7 +199,7 @@ defmodule Plausible.Stats do
|
||||
) |> Enum.into(%{})
|
||||
|
||||
bounced_sessions_by_referrer = Repo.all(
|
||||
from s in Plausible.Session,
|
||||
from s in Plausible.FingerprintSession,
|
||||
where: s.domain == ^site.domain,
|
||||
where: s.start >= ^first_datetime and s.start < ^last_datetime,
|
||||
where: s.is_bounce,
|
||||
@ -223,7 +223,7 @@ defmodule Plausible.Stats do
|
||||
def visitors_from_referrer(site, query, referrer) do
|
||||
Repo.one(
|
||||
from e in base_query(site, query),
|
||||
select: count(e.user_id, :distinct),
|
||||
select: count(e.fingerprint, :distinct),
|
||||
where: e.referrer_source == ^referrer
|
||||
)
|
||||
end
|
||||
@ -231,7 +231,7 @@ defmodule Plausible.Stats do
|
||||
def conversions_from_referrer(site, query, referrer) do
|
||||
Repo.one(
|
||||
from e in base_query(site, query),
|
||||
select: count(e.user_id, :distinct),
|
||||
select: count(e.fingerprint, :distinct),
|
||||
where: e.initial_referrer_source == ^referrer
|
||||
)
|
||||
end
|
||||
@ -239,7 +239,7 @@ defmodule Plausible.Stats do
|
||||
def referrer_drilldown(site, query, referrer, include \\ []) do
|
||||
referring_urls = Repo.all(
|
||||
from e in base_query(site, query),
|
||||
select: %{name: e.referrer, count: count(e.user_id, :distinct)},
|
||||
select: %{name: e.referrer, count: count(e.fingerprint, :distinct)},
|
||||
group_by: e.referrer,
|
||||
where: e.referrer_source == ^referrer,
|
||||
order_by: [desc: 2],
|
||||
@ -275,7 +275,7 @@ defmodule Plausible.Stats do
|
||||
def referrer_drilldown_for_goal(site, query, referrer) do
|
||||
Repo.all(
|
||||
from e in base_query(site, query),
|
||||
select: %{name: e.initial_referrer, count: count(e.user_id, :distinct)},
|
||||
select: %{name: e.initial_referrer, count: count(e.fingerprint, :distinct)},
|
||||
group_by: e.initial_referrer,
|
||||
where: e.initial_referrer_source == ^referrer,
|
||||
order_by: [desc: 2],
|
||||
@ -287,7 +287,7 @@ defmodule Plausible.Stats do
|
||||
{first_datetime, last_datetime} = date_range_utc_boundaries(query.date_range, site.timezone)
|
||||
|
||||
total_sessions_by_url = Repo.all(
|
||||
from s in Plausible.Session,
|
||||
from s in Plausible.FingerprintSession,
|
||||
where: s.domain == ^site.domain,
|
||||
where: s.start >= ^first_datetime and s.start < ^last_datetime,
|
||||
where: s.referrer in ^referring_urls,
|
||||
@ -296,7 +296,7 @@ defmodule Plausible.Stats do
|
||||
) |> Enum.into(%{})
|
||||
|
||||
bounced_sessions_by_url = Repo.all(
|
||||
from s in Plausible.Session,
|
||||
from s in Plausible.FingerprintSession,
|
||||
where: s.domain == ^site.domain,
|
||||
where: s.start >= ^first_datetime and s.start < ^last_datetime,
|
||||
where: s.is_bounce,
|
||||
@ -340,7 +340,7 @@ defmodule Plausible.Stats do
|
||||
{first_datetime, last_datetime} = date_range_utc_boundaries(query.date_range, site.timezone)
|
||||
|
||||
total_sessions_by_url = Repo.all(
|
||||
from s in Plausible.Session,
|
||||
from s in Plausible.FingerprintSession,
|
||||
where: s.domain == ^site.domain,
|
||||
where: s.start >= ^first_datetime and s.start < ^last_datetime,
|
||||
where: s.entry_page in ^page_urls,
|
||||
@ -349,7 +349,7 @@ defmodule Plausible.Stats do
|
||||
) |> Enum.into(%{})
|
||||
|
||||
bounced_sessions_by_url = Repo.all(
|
||||
from s in Plausible.Session,
|
||||
from s in Plausible.FingerprintSession,
|
||||
where: s.domain == ^site.domain,
|
||||
where: s.start >= ^first_datetime and s.start < ^last_datetime,
|
||||
where: s.is_bounce,
|
||||
@ -381,7 +381,7 @@ defmodule Plausible.Stats do
|
||||
|
||||
def top_screen_sizes(site, query) do
|
||||
Repo.all(from e in base_query(site, query),
|
||||
select: %{name: e.screen_size, count: count(e.user_id, :distinct)},
|
||||
select: %{name: e.screen_size, count: count(e.fingerprint, :distinct)},
|
||||
group_by: e.screen_size,
|
||||
where: not is_nil(e.screen_size)
|
||||
)
|
||||
@ -395,7 +395,7 @@ defmodule Plausible.Stats do
|
||||
|
||||
def countries(site, query) do
|
||||
Repo.all(from e in base_query(site, query),
|
||||
select: %{name: e.country_code, count: count(e.user_id, :distinct)},
|
||||
select: %{name: e.country_code, count: count(e.fingerprint, :distinct)},
|
||||
group_by: e.country_code,
|
||||
where: not is_nil(e.country_code),
|
||||
order_by: [desc: 2]
|
||||
@ -411,7 +411,7 @@ defmodule Plausible.Stats do
|
||||
|
||||
def browsers(site, query, limit \\ 5) do
|
||||
Repo.all(from e in base_query(site, query),
|
||||
select: %{name: e.browser, count: count(e.user_id, :distinct)},
|
||||
select: %{name: e.browser, count: count(e.fingerprint, :distinct)},
|
||||
group_by: e.browser,
|
||||
where: not is_nil(e.browser),
|
||||
order_by: [desc: 2]
|
||||
@ -422,7 +422,7 @@ defmodule Plausible.Stats do
|
||||
|
||||
def operating_systems(site, query, limit \\ 5) do
|
||||
Repo.all(from e in base_query(site, query),
|
||||
select: %{name: e.operating_system, count: count(e.user_id, :distinct)},
|
||||
select: %{name: e.operating_system, count: count(e.fingerprint, :distinct)},
|
||||
group_by: e.operating_system,
|
||||
where: not is_nil(e.operating_system),
|
||||
order_by: [desc: 2]
|
||||
@ -436,13 +436,13 @@ defmodule Plausible.Stats do
|
||||
from e in Plausible.Event,
|
||||
where: e.timestamp >= fragment("(now() at time zone 'utc') - '5 minutes'::interval"),
|
||||
where: e.domain == ^site.domain,
|
||||
select: count(e.user_id, :distinct)
|
||||
select: count(e.fingerprint, :distinct)
|
||||
)
|
||||
end
|
||||
|
||||
def goal_conversions(site, %Query{filters: %{"goal" => goal}} = query) when is_binary(goal) do
|
||||
Repo.all(from e in base_query(site, query),
|
||||
select: count(e.user_id, :distinct),
|
||||
select: count(e.fingerprint, :distinct),
|
||||
group_by: e.name,
|
||||
order_by: [desc: 1]
|
||||
) |> Enum.map(fn count -> %{name: goal, count: count} end)
|
||||
@ -463,7 +463,7 @@ defmodule Plausible.Stats do
|
||||
Repo.all(
|
||||
from e in base_query(site, query, events),
|
||||
group_by: e.name,
|
||||
select: %{name: e.name, count: count(e.user_id, :distinct)}
|
||||
select: %{name: e.name, count: count(e.fingerprint, :distinct)}
|
||||
)
|
||||
else
|
||||
[]
|
||||
@ -479,7 +479,7 @@ defmodule Plausible.Stats do
|
||||
from e in base_query(site, query),
|
||||
where: e.pathname in ^pages,
|
||||
group_by: e.pathname,
|
||||
select: %{name: fragment("concat('Visit ', ?)", e.pathname), count: count(e.user_id, :distinct)}
|
||||
select: %{name: fragment("concat('Visit ', ?)", e.pathname), count: count(e.fingerprint, :distinct)}
|
||||
)
|
||||
else
|
||||
[]
|
||||
|
@ -9,9 +9,7 @@ defmodule PlausibleWeb.Api.ExternalController do
|
||||
{:ok, nil} ->
|
||||
conn |> send_resp(202, "")
|
||||
{:ok, event} ->
|
||||
Plausible.Ingest.Session.on_event(event)
|
||||
Plausible.Ingest.FingerprintSession.on_event(event)
|
||||
update_fingerprint(event)
|
||||
conn |> send_resp(202, "")
|
||||
{:error, changeset} ->
|
||||
request = Sentry.Plug.build_request_interface_data(conn, [])
|
||||
@ -23,7 +21,6 @@ defmodule PlausibleWeb.Api.ExternalController do
|
||||
|
||||
def unload(conn, _params) do
|
||||
params = parse_body(conn)
|
||||
Plausible.Ingest.Session.on_unload(params["uid"], Timex.now())
|
||||
fingerprint = calculate_fingerprint(conn, params)
|
||||
Plausible.Ingest.FingerprintSession.on_unload(fingerprint, Timex.now())
|
||||
conn |> send_resp(202, "")
|
||||
@ -35,13 +32,6 @@ defmodule PlausibleWeb.Api.ExternalController do
|
||||
send_resp(conn, 200, "")
|
||||
end
|
||||
|
||||
defp update_fingerprint(%Plausible.Event{new_visitor: true}), do: nil
|
||||
defp update_fingerprint(event) do
|
||||
use Plausible.Repo
|
||||
q = from(e in Plausible.Event, where: e.user_id == ^event.user_id and is_nil(e.fingerprint))
|
||||
Repo.update_all(q, set: [fingerprint: event.fingerprint])
|
||||
end
|
||||
|
||||
defp create_event(conn, params) do
|
||||
uri = URI.parse(params["url"])
|
||||
country_code = Plug.Conn.get_req_header(conn, "cf-ipcountry") |> List.first
|
||||
|
@ -1,4 +1,4 @@
|
||||
<%= if Mix.env() == :prod && !@conn.assigns[:skip_plausible_tracking] do %>
|
||||
<script async defer src="https://plausible.io/js/plausible.js"></script>
|
||||
<%= if !@conn.assigns[:skip_plausible_tracking] do %>
|
||||
<script async defer data-track-acquisition src="http://localtest.me:8000/js/plausible.js"></script>
|
||||
<script>window.plausible = window.plausible || function() { (window.plausible.q = window.plausible.q || []).push(arguments) }</script>
|
||||
<% end %>
|
||||
|
@ -0,0 +1,14 @@
|
||||
defmodule Plausible.Repo.Migrations.BackfillFingerprints do
|
||||
use Ecto.Migration
|
||||
|
||||
def change do
|
||||
execute "UPDATE events set fingerprint=user_id where fingerprint is null"
|
||||
|
||||
execute """
|
||||
INSERT INTO fingerprint_sessions (hostname, domain, fingerprint, start, length, is_bounce, entry_page, exit_page, referrer, referrer_source, country_code, screen_size, operating_system, browser, timestamp)
|
||||
SELECT hostname, domain, user_id, start, length, is_bounce, entry_page, exit_page, referrer, referrer_source, country_code, screen_size, operating_system, browser, timestamp
|
||||
FROM sessions
|
||||
WHERE sessions.timestamp < '2020-02-27 11:40:55';
|
||||
"""
|
||||
end
|
||||
end
|
80
test/plausible/ingest/fingerprint_session_test.exs
Normal file
80
test/plausible/ingest/fingerprint_session_test.exs
Normal file
@ -0,0 +1,80 @@
|
||||
defmodule Plausible.Ingest.FingerprintSessionTest do
|
||||
use Plausible.DataCase
|
||||
alias Plausible.Ingest
|
||||
|
||||
defp capture_session(fingerprint) do
|
||||
session_pid = :global.whereis_name(fingerprint)
|
||||
Process.monitor(session_pid)
|
||||
|
||||
assert_receive({:DOWN, session_pid, :process, _, :normal})
|
||||
|
||||
Repo.one(Plausible.FingerprintSession)
|
||||
end
|
||||
|
||||
describe "on_event/1" do
|
||||
test "starts a new session if there is no session for user id" do
|
||||
pageview = insert(:pageview)
|
||||
|
||||
refute is_pid(:global.whereis_name(pageview.fingerprint))
|
||||
|
||||
Ingest.FingerprintSession.on_event(pageview)
|
||||
|
||||
assert is_pid(:global.whereis_name(pageview.fingerprint))
|
||||
end
|
||||
|
||||
test "copies event data to session" do
|
||||
pageview = insert(:pageview)
|
||||
|
||||
Ingest.FingerprintSession.on_event(pageview)
|
||||
|
||||
session = capture_session(pageview.fingerprint)
|
||||
|
||||
assert session.fingerprint == pageview.fingerprint
|
||||
assert session.start == pageview.timestamp
|
||||
end
|
||||
|
||||
test "inserts bounced session when timeout fires after one pageview" do
|
||||
pageview = insert(:pageview)
|
||||
|
||||
Ingest.FingerprintSession.on_event(pageview)
|
||||
|
||||
session = capture_session(pageview.fingerprint)
|
||||
assert session.is_bounce
|
||||
end
|
||||
|
||||
test "session with two events is not a bounce" do
|
||||
pageview = insert(:pageview)
|
||||
pageview2 = insert(:pageview, fingerprint: pageview.fingerprint)
|
||||
|
||||
Ingest.FingerprintSession.on_event(pageview)
|
||||
Ingest.FingerprintSession.on_event(pageview2)
|
||||
|
||||
session = capture_session(pageview.fingerprint)
|
||||
refute session.is_bounce
|
||||
end
|
||||
|
||||
test "captures the exit page" do
|
||||
pageview = insert(:pageview)
|
||||
pageview2 = insert(:pageview, fingerprint: pageview.fingerprint, pathname: "/exit")
|
||||
|
||||
Ingest.FingerprintSession.on_event(pageview)
|
||||
Ingest.FingerprintSession.on_event(pageview2)
|
||||
|
||||
session = capture_session(pageview.fingerprint)
|
||||
assert session.exit_page == "/exit"
|
||||
end
|
||||
end
|
||||
|
||||
describe "on_unload/1" do
|
||||
test "uses the unload timestamp to calculate session length" do
|
||||
pageview = insert(:pageview)
|
||||
unload_timestamp = Timex.shift(pageview.timestamp, seconds: 30)
|
||||
|
||||
Ingest.FingerprintSession.on_event(pageview)
|
||||
Ingest.FingerprintSession.on_unload(pageview.fingerprint, unload_timestamp)
|
||||
|
||||
session = capture_session(pageview.fingerprint)
|
||||
assert session.length == 30
|
||||
end
|
||||
end
|
||||
end
|
@ -2,8 +2,8 @@ defmodule PlausibleWeb.Api.ExternalControllerTest do
|
||||
use PlausibleWeb.ConnCase
|
||||
use Plausible.Repo
|
||||
|
||||
defp finalize_session(user_id) do
|
||||
session_pid = :global.whereis_name(user_id)
|
||||
defp finalize_session(fingerprint) do
|
||||
session_pid = :global.whereis_name(fingerprint)
|
||||
Process.monitor(session_pid)
|
||||
|
||||
assert_receive({:DOWN, session_pid, _, _, _})
|
||||
@ -12,28 +12,6 @@ defmodule PlausibleWeb.Api.ExternalControllerTest do
|
||||
@user_agent "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.110 Safari/537.36"
|
||||
@country_code "EE"
|
||||
|
||||
test "TEMP - adding historical fingerprints", %{conn: conn} do
|
||||
uid = UUID.uuid4()
|
||||
insert(:pageview, domain: "gigride.live", user_id: uid, new_visitor: true)
|
||||
|
||||
params = %{
|
||||
name: "pageview",
|
||||
url: "http://gigride.live/",
|
||||
new_visitor: false,
|
||||
uid: uid
|
||||
}
|
||||
|
||||
conn
|
||||
|> put_req_header("content-type", "text/plain")
|
||||
|> put_req_header("user-agent", @user_agent)
|
||||
|> post("/api/event", Jason.encode!(params))
|
||||
|
||||
[pageview1, pageview2] = Repo.all(Plausible.Event)
|
||||
finalize_session(uid)
|
||||
|
||||
assert pageview1.fingerprint == pageview2.fingerprint
|
||||
end
|
||||
|
||||
describe "POST /api/event" do
|
||||
test "records the event", %{conn: conn} do
|
||||
params = %{
|
||||
@ -52,7 +30,7 @@ defmodule PlausibleWeb.Api.ExternalControllerTest do
|
||||
|> post("/api/event", Jason.encode!(params))
|
||||
|
||||
pageview = Repo.one(Plausible.Event)
|
||||
finalize_session(pageview.user_id)
|
||||
finalize_session(pageview.fingerprint)
|
||||
|
||||
assert response(conn, 202) == ""
|
||||
assert pageview.hostname == "gigride.live"
|
||||
@ -77,7 +55,7 @@ defmodule PlausibleWeb.Api.ExternalControllerTest do
|
||||
|> post("/api/event", Jason.encode!(params))
|
||||
|
||||
event = Repo.one(Plausible.Event)
|
||||
finalize_session(event.user_id)
|
||||
finalize_session(event.fingerprint)
|
||||
|
||||
assert response(conn, 202) == ""
|
||||
assert event.domain == "some_site.com"
|
||||
@ -97,7 +75,7 @@ defmodule PlausibleWeb.Api.ExternalControllerTest do
|
||||
|> post("/api/event", Jason.encode!(params))
|
||||
|
||||
pageview = Repo.one(Plausible.Event)
|
||||
finalize_session(pageview.user_id)
|
||||
finalize_session(pageview.fingerprint)
|
||||
|
||||
assert pageview.domain == "some_site.com"
|
||||
end
|
||||
@ -115,7 +93,7 @@ defmodule PlausibleWeb.Api.ExternalControllerTest do
|
||||
|> post("/api/event", Jason.encode!(params))
|
||||
|
||||
pageview = Repo.one(Plausible.Event)
|
||||
finalize_session(pageview.user_id)
|
||||
finalize_session(pageview.fingerprint)
|
||||
|
||||
assert pageview.hostname == "example.com"
|
||||
end
|
||||
@ -151,7 +129,7 @@ defmodule PlausibleWeb.Api.ExternalControllerTest do
|
||||
|> post("/api/event", Jason.encode!(params))
|
||||
|
||||
pageview = Repo.one(Plausible.Event)
|
||||
finalize_session(pageview.user_id)
|
||||
finalize_session(pageview.fingerprint)
|
||||
|
||||
assert response(conn, 202) == ""
|
||||
assert pageview.operating_system == "Mac"
|
||||
@ -174,7 +152,7 @@ defmodule PlausibleWeb.Api.ExternalControllerTest do
|
||||
|> post("/api/event", Jason.encode!(params))
|
||||
|
||||
pageview = Repo.one(Plausible.Event)
|
||||
finalize_session(pageview.user_id)
|
||||
finalize_session(pageview.fingerprint)
|
||||
|
||||
assert response(conn, 202) == ""
|
||||
assert pageview.referrer_source == "Facebook"
|
||||
@ -197,7 +175,7 @@ defmodule PlausibleWeb.Api.ExternalControllerTest do
|
||||
|> post("/api/event", Jason.encode!(params))
|
||||
|
||||
pageview = Repo.one(Plausible.Event)
|
||||
finalize_session(pageview.user_id)
|
||||
finalize_session(pageview.fingerprint)
|
||||
|
||||
assert response(conn, 202) == ""
|
||||
assert pageview.referrer == "facebook.com/page"
|
||||
@ -222,7 +200,7 @@ defmodule PlausibleWeb.Api.ExternalControllerTest do
|
||||
|> post("/api/event", Jason.encode!(params))
|
||||
|
||||
pageview = Repo.one(Plausible.Event)
|
||||
finalize_session(pageview.user_id)
|
||||
finalize_session(pageview.fingerprint)
|
||||
|
||||
assert response(conn, 202) == ""
|
||||
assert pageview.referrer_source == nil
|
||||
@ -245,7 +223,7 @@ defmodule PlausibleWeb.Api.ExternalControllerTest do
|
||||
|> post("/api/event", Jason.encode!(params))
|
||||
|
||||
pageview = Repo.one(Plausible.Event)
|
||||
finalize_session(pageview.user_id)
|
||||
finalize_session(pageview.fingerprint)
|
||||
|
||||
assert response(conn, 202) == ""
|
||||
assert pageview.referrer_source == nil
|
||||
@ -268,7 +246,7 @@ defmodule PlausibleWeb.Api.ExternalControllerTest do
|
||||
|> post("/api/event", Jason.encode!(params))
|
||||
|
||||
pageview = Repo.one(Plausible.Event)
|
||||
finalize_session(pageview.user_id)
|
||||
finalize_session(pageview.fingerprint)
|
||||
|
||||
assert response(conn, 202) == ""
|
||||
assert pageview.referrer_source == "blog.gigride.live"
|
||||
@ -290,7 +268,7 @@ defmodule PlausibleWeb.Api.ExternalControllerTest do
|
||||
|> post("/api/event", Jason.encode!(params))
|
||||
|
||||
pageview = Repo.one(Plausible.Event)
|
||||
finalize_session(pageview.user_id)
|
||||
finalize_session(pageview.fingerprint)
|
||||
|
||||
assert pageview.referrer == "indiehackers.com/page"
|
||||
assert pageview.initial_referrer == "indiehackers.com"
|
||||
@ -312,7 +290,7 @@ defmodule PlausibleWeb.Api.ExternalControllerTest do
|
||||
|> post("/api/event", Jason.encode!(params))
|
||||
|
||||
pageview = Repo.one(Plausible.Event)
|
||||
finalize_session(pageview.user_id)
|
||||
finalize_session(pageview.fingerprint)
|
||||
|
||||
assert pageview.referrer_source == "betalist"
|
||||
assert pageview.initial_referrer_source == "betalist"
|
||||
@ -333,7 +311,7 @@ defmodule PlausibleWeb.Api.ExternalControllerTest do
|
||||
|> post("/api/event", Jason.encode!(params))
|
||||
|
||||
pageview = Repo.one(Plausible.Event)
|
||||
finalize_session(pageview.user_id)
|
||||
finalize_session(pageview.fingerprint)
|
||||
|
||||
assert response(conn, 202) == ""
|
||||
assert pageview.referrer_source == "indiehackers.com"
|
||||
@ -354,7 +332,7 @@ defmodule PlausibleWeb.Api.ExternalControllerTest do
|
||||
|> post("/api/event", Jason.encode!(params))
|
||||
|
||||
pageview = Repo.one(Plausible.Event)
|
||||
finalize_session(pageview.user_id)
|
||||
finalize_session(pageview.fingerprint)
|
||||
|
||||
assert response(conn, 202) == ""
|
||||
assert is_nil(pageview.referrer_source)
|
||||
@ -377,7 +355,7 @@ defmodule PlausibleWeb.Api.ExternalControllerTest do
|
||||
|> post("/api/event", Jason.encode!(params))
|
||||
|
||||
pageview = Repo.one(Plausible.Event)
|
||||
finalize_session(pageview.user_id)
|
||||
finalize_session(pageview.fingerprint)
|
||||
|
||||
assert response(conn, 202) == ""
|
||||
assert pageview.screen_size == "Mobile"
|
||||
@ -397,7 +375,7 @@ defmodule PlausibleWeb.Api.ExternalControllerTest do
|
||||
|> post("/api/event", Jason.encode!(params))
|
||||
|
||||
pageview = Repo.one(Plausible.Event)
|
||||
finalize_session(pageview.user_id)
|
||||
finalize_session(pageview.fingerprint)
|
||||
|
||||
assert response(conn, 202) == ""
|
||||
assert pageview.screen_size == nil
|
||||
@ -417,7 +395,7 @@ defmodule PlausibleWeb.Api.ExternalControllerTest do
|
||||
|> post("/api/event", Jason.encode!(params))
|
||||
|
||||
event = Repo.one(Plausible.Event)
|
||||
finalize_session(event.user_id)
|
||||
finalize_session(event.fingerprint)
|
||||
|
||||
assert response(conn, 202) == ""
|
||||
assert event.name == "custom event"
|
||||
|
@ -8,7 +8,7 @@ defmodule PlausibleWeb.Api.StatsController.CurrentVisitorsTest do
|
||||
test "returns unique users in the last 5 minutes", %{conn: conn, site: site} do
|
||||
insert(:pageview, domain: site.domain)
|
||||
event2 = insert(:pageview, domain: site.domain, timestamp: Timex.now() |> Timex.shift(minutes: -3))
|
||||
insert(:pageview, domain: site.domain, user_id: event2.user_id, timestamp: Timex.now() |> Timex.shift(minutes: -4))
|
||||
insert(:pageview, domain: site.domain, fingerprint: event2.fingerprint, timestamp: Timex.now() |> Timex.shift(minutes: -4))
|
||||
insert(:pageview, domain: site.domain, timestamp: Timex.now() |> Timex.shift(minutes: -6))
|
||||
|
||||
conn = get(conn, "/api/stats/#{site.domain}/current-visitors?period=day&date=2019-01-01")
|
||||
|
@ -88,8 +88,8 @@ defmodule PlausibleWeb.Api.StatsController.MainGraphTest do
|
||||
setup [:create_user, :log_in, :create_site]
|
||||
|
||||
test "unique users counts distinct user ids", %{conn: conn, site: site} do
|
||||
insert(:pageview, domain: site.domain, user_id: @user_id, timestamp: ~N[2019-01-01 00:00:00])
|
||||
insert(:pageview, domain: site.domain, user_id: @user_id, timestamp: ~N[2019-01-01 23:59:00])
|
||||
insert(:pageview, domain: site.domain, fingerprint: @user_id, timestamp: ~N[2019-01-01 00:00:00])
|
||||
insert(:pageview, domain: site.domain, fingerprint: @user_id, timestamp: ~N[2019-01-01 23:59:00])
|
||||
|
||||
conn = get(conn, "/api/stats/#{site.domain}/main-graph?period=day&date=2019-01-01")
|
||||
|
||||
@ -108,8 +108,8 @@ defmodule PlausibleWeb.Api.StatsController.MainGraphTest do
|
||||
end
|
||||
|
||||
test "counts total pageviews even from same user ids", %{conn: conn, site: site} do
|
||||
insert(:pageview, domain: site.domain, user_id: @user_id, timestamp: ~N[2019-01-01 00:00:00])
|
||||
insert(:pageview, domain: site.domain, user_id: @user_id, timestamp: ~N[2019-01-01 23:59:00])
|
||||
insert(:pageview, domain: site.domain, fingerprint: @user_id, timestamp: ~N[2019-01-01 00:00:00])
|
||||
insert(:pageview, domain: site.domain, fingerprint: @user_id, timestamp: ~N[2019-01-01 23:59:00])
|
||||
|
||||
conn = get(conn, "/api/stats/#{site.domain}/main-graph?period=day&date=2019-01-01")
|
||||
|
||||
@ -160,8 +160,8 @@ defmodule PlausibleWeb.Api.StatsController.MainGraphTest do
|
||||
|
||||
test "returns total unique visitors", %{conn: conn, site: site} do
|
||||
insert(:pageview, domain: site.domain, timestamp: ~N[2019-01-01 02:00:00])
|
||||
insert(:pageview, domain: site.domain, user_id: @user_id, timestamp: ~N[2019-01-01 01:00:00])
|
||||
insert(:event, name: "Signup", domain: site.domain, user_id: @user_id, timestamp: ~N[2019-01-01 02:00:00])
|
||||
insert(:pageview, domain: site.domain, fingerprint: @user_id, timestamp: ~N[2019-01-01 01:00:00])
|
||||
insert(:event, name: "Signup", domain: site.domain, fingerprint: @user_id, timestamp: ~N[2019-01-01 02:00:00])
|
||||
|
||||
filters = Jason.encode!(%{goal: "Signup"})
|
||||
conn = get(conn, "/api/stats/#{site.domain}/main-graph?period=day&date=2019-01-01&filters=#{filters}")
|
||||
|
@ -7,7 +7,7 @@ defmodule PlausibleWeb.Api.StatsController.ReferrersTest do
|
||||
|
||||
test "returns top referrer sources by user ids", %{conn: conn, site: site} do
|
||||
pageview1 = insert(:pageview, domain: site.domain, referrer_source: "Google", timestamp: ~N[2019-01-01 01:00:00])
|
||||
insert(:pageview, domain: site.domain, referrer_source: "Google", user_id: pageview1.user_id, timestamp: ~N[2019-01-01 02:00:00])
|
||||
insert(:pageview, domain: site.domain, referrer_source: "Google", fingerprint: pageview1.fingerprint, timestamp: ~N[2019-01-01 02:00:00])
|
||||
insert(:pageview, domain: site.domain, referrer_source: "Google", timestamp: ~N[2019-01-01 02:00:00])
|
||||
insert(:pageview, domain: site.domain, referrer_source: "Bing", timestamp: ~N[2019-01-01 02:00:00])
|
||||
|
||||
|
@ -26,12 +26,11 @@ defmodule Plausible.Factory do
|
||||
def session_factory do
|
||||
hostname = sequence(:domain, &"example-#{&1}.com")
|
||||
|
||||
%Plausible.Session{
|
||||
%Plausible.FingerprintSession{
|
||||
hostname: hostname,
|
||||
domain: hostname,
|
||||
new_visitor: true,
|
||||
entry_page: "/",
|
||||
user_id: UUID.uuid4(),
|
||||
fingerprint: UUID.uuid4(),
|
||||
start: Timex.now(),
|
||||
is_bounce: false
|
||||
}
|
||||
@ -53,7 +52,9 @@ defmodule Plausible.Factory do
|
||||
hostname: hostname,
|
||||
domain: hostname,
|
||||
pathname: "/",
|
||||
new_visitor: true, user_id: UUID.uuid4(),
|
||||
new_visitor: true,
|
||||
user_id: UUID.uuid4(),
|
||||
fingerprint: UUID.uuid4()
|
||||
}
|
||||
end
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user