mirror of
https://github.com/plausible/analytics.git
synced 2024-10-26 18:32:25 +03:00
Experimental session count (#3786)
* WIP * Allow `experimetnal_session_count` request serialization * Extend `Plausible.Stats.Query` with `experimental_session_count` flag * Add `FunWithFlags` actor implementation for `Site` * Change the way sessions are retrieved * Remove redundant test * Format * Update the test --------- Co-authored-by: Uku Taht <uku.taht@gmail.com>
This commit is contained in:
parent
672d682e95
commit
926de4dd10
@ -1,4 +1,4 @@
|
||||
import {formatISO} from './util/date'
|
||||
import { formatISO } from './util/date'
|
||||
|
||||
let abortController = new AbortController()
|
||||
let SHARED_LINK_AUTH = null
|
||||
@ -36,14 +36,15 @@ function serializeFilters(filters) {
|
||||
return JSON.stringify(cleaned)
|
||||
}
|
||||
|
||||
export function serializeQuery(query, extraQuery=[]) {
|
||||
export function serializeQuery(query, extraQuery = []) {
|
||||
const queryObj = {}
|
||||
if (query.period) { queryObj.period = query.period }
|
||||
if (query.date) { queryObj.date = formatISO(query.date) }
|
||||
if (query.from) { queryObj.from = formatISO(query.from) }
|
||||
if (query.to) { queryObj.to = formatISO(query.to) }
|
||||
if (query.filters) { queryObj.filters = serializeFilters(query.filters) }
|
||||
if (query.with_imported) { queryObj.with_imported = query.with_imported }
|
||||
if (query.period) { queryObj.period = query.period }
|
||||
if (query.date) { queryObj.date = formatISO(query.date) }
|
||||
if (query.from) { queryObj.from = formatISO(query.from) }
|
||||
if (query.to) { queryObj.to = formatISO(query.to) }
|
||||
if (query.filters) { queryObj.filters = serializeFilters(query.filters) }
|
||||
if (query.experimental_session_count) { queryObj.experimental_session_count = query.experimental_session_count }
|
||||
if (query.with_imported) { queryObj.with_imported = query.with_imported }
|
||||
if (SHARED_LINK_AUTH) { queryObj.auth = SHARED_LINK_AUTH }
|
||||
|
||||
if (query.comparison) {
|
||||
@ -58,11 +59,11 @@ export function serializeQuery(query, extraQuery=[]) {
|
||||
return '?' + serialize(queryObj)
|
||||
}
|
||||
|
||||
export function get(url, query={}, ...extraQuery) {
|
||||
const headers = SHARED_LINK_AUTH ? {'X-Shared-Link-Auth': SHARED_LINK_AUTH} : {}
|
||||
export function get(url, query = {}, ...extraQuery) {
|
||||
const headers = SHARED_LINK_AUTH ? { 'X-Shared-Link-Auth': SHARED_LINK_AUTH } : {}
|
||||
url = url + serializeQuery(query, extraQuery)
|
||||
return fetch(url, {signal: abortController.signal, headers: headers})
|
||||
.then( response => {
|
||||
return fetch(url, { signal: abortController.signal, headers: headers })
|
||||
.then(response => {
|
||||
if (!response.ok) {
|
||||
return response.json().then((msg) => {
|
||||
throw new ApiError(msg.error, msg)
|
||||
|
@ -39,6 +39,7 @@ export function parseQuery(querystring, site) {
|
||||
to: q.get('to') ? dayjs.utc(q.get('to')) : undefined,
|
||||
match_day_of_week: matchDayOfWeek == 'true',
|
||||
with_imported: q.get('with_imported') ? q.get('with_imported') === 'true' : true,
|
||||
experimental_session_count: q.get('experimental_session_count'),
|
||||
filters: {
|
||||
'goal': q.get('goal'),
|
||||
'props': JSON.parse(q.get('props')),
|
||||
|
@ -242,3 +242,9 @@ defmodule Plausible.Site do
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
defimpl FunWithFlags.Actor, for: Plausible.Site do
|
||||
def id(%{domain: domain}) do
|
||||
"site:#{domain}"
|
||||
end
|
||||
end
|
||||
|
@ -132,14 +132,18 @@ defmodule Plausible.Stats.Base do
|
||||
}
|
||||
|
||||
def query_sessions(site, query) do
|
||||
{first_datetime, last_datetime} = utc_boundaries(query, site)
|
||||
{first_datetime, last_datetime} =
|
||||
utc_boundaries(query, site)
|
||||
|
||||
q = from(s in "sessions_v2", where: s.site_id == ^site.id)
|
||||
|
||||
sessions_q =
|
||||
from(
|
||||
s in "sessions_v2",
|
||||
where: s.site_id == ^site.id,
|
||||
where: s.start >= ^first_datetime and s.start < ^last_datetime
|
||||
)
|
||||
if FunWithFlags.enabled?(:experimental_session_count, for: site) or
|
||||
query.experimental_session_count? do
|
||||
from s in q, where: s.timestamp >= ^first_datetime and s.start < ^last_datetime
|
||||
else
|
||||
from s in q, where: s.start >= ^first_datetime and s.start < ^last_datetime
|
||||
end
|
||||
|
||||
on_full_build do
|
||||
sessions_q = Plausible.Stats.Sampling.add_query_hint(sessions_q, query)
|
||||
|
@ -8,7 +8,8 @@ defmodule Plausible.Stats.Query do
|
||||
sample_threshold: 20_000_000,
|
||||
imported_data_requested: false,
|
||||
include_imported: false,
|
||||
now: nil
|
||||
now: nil,
|
||||
experimental_session_count?: false
|
||||
|
||||
require OpenTelemetry.Tracer, as: Tracer
|
||||
alias Plausible.Stats.{Filters, Interval}
|
||||
@ -21,6 +22,7 @@ defmodule Plausible.Stats.Query do
|
||||
query =
|
||||
__MODULE__
|
||||
|> struct!(now: now)
|
||||
|> put_experimental_session_count(params)
|
||||
|> put_period(site, params)
|
||||
|> put_interval(params)
|
||||
|> put_parsed_filters(params)
|
||||
@ -34,6 +36,14 @@ defmodule Plausible.Stats.Query do
|
||||
query
|
||||
end
|
||||
|
||||
defp put_experimental_session_count(query, params) do
|
||||
if Map.get(params, "experimental_session_count") == "true" do
|
||||
struct!(query, experimental_session_count?: true)
|
||||
else
|
||||
query
|
||||
end
|
||||
end
|
||||
|
||||
defp put_period(query, site, %{"period" => "realtime"}) do
|
||||
date = today(site.timezone)
|
||||
|
||||
|
@ -40,19 +40,32 @@ defmodule PlausibleWeb.Api.StatsController.TopStatsTest do
|
||||
} in res["top_stats"]
|
||||
end
|
||||
|
||||
test "counts total visits", %{conn: conn, site: site} do
|
||||
test "experimental session count", %{conn: conn, site: site} do
|
||||
populate_stats(site, [
|
||||
build(:pageview, user_id: @user_id, timestamp: ~N[2021-01-01 00:00:00]),
|
||||
build(:pageview, user_id: 3421, timestamp: ~N[2020-12-31 23:30:00]),
|
||||
build(:pageview, user_id: @user_id, timestamp: ~N[2020-12-31 23:59:00]),
|
||||
build(:pageview, user_id: @user_id, timestamp: ~N[2021-01-01 00:01:00]),
|
||||
build(:pageview, user_id: @user_id, timestamp: ~N[2021-01-01 10:00:00]),
|
||||
build(:pageview, timestamp: ~N[2021-01-01 15:00:00])
|
||||
build(:pageview, user_id: 2, timestamp: ~N[2020-12-31 23:59:00]),
|
||||
build(:pageview, user_id: 2, timestamp: ~N[2021-01-01 00:01:00]),
|
||||
build(:pageview, user_id: 617_235, timestamp: ~N[2021-01-03 00:00:00])
|
||||
])
|
||||
|
||||
conn = get(conn, "/api/stats/#{site.domain}/top-stats?period=day&date=2021-01-01")
|
||||
conn =
|
||||
get(
|
||||
conn,
|
||||
"/api/stats/#{site.domain}/top-stats?period=day&date=2021-01-01&experimental_session_count=true"
|
||||
)
|
||||
|
||||
res = json_response(conn, 200)
|
||||
|
||||
assert %{"name" => "Total visits", "value" => 3} in res["top_stats"]
|
||||
assert res["top_stats"] == [
|
||||
%{"name" => "Unique visitors", "value" => 2},
|
||||
%{"name" => "Total visits", "value" => 2},
|
||||
%{"name" => "Total pageviews", "value" => 2},
|
||||
%{"name" => "Views per visit", "value" => 2.0},
|
||||
%{"name" => "Bounce rate", "value" => 0},
|
||||
%{"name" => "Visit duration", "value" => 120}
|
||||
]
|
||||
end
|
||||
|
||||
test "counts pages per visit", %{conn: conn, site: site} do
|
||||
|
Loading…
Reference in New Issue
Block a user