0.2.45 - Server side git commits
This commit is contained in:
@@ -97,6 +97,28 @@ type PathStat struct {
|
||||
Count int64 `json:"count"`
|
||||
}
|
||||
|
||||
// JourneyStats is the server-derived health of foreground visits in a reporting window.
|
||||
// An unfinished visit is only abandoned once it has been quiet for thirty minutes; until
|
||||
// then it is active, so an open television does not immediately look like a failed flow.
|
||||
type JourneyStats struct {
|
||||
Events int64 `json:"events"`
|
||||
Journeys int64 `json:"journeys"`
|
||||
Viewers int64 `json:"viewers"`
|
||||
Completed int64 `json:"completed"`
|
||||
Abandoned int64 `json:"abandoned"`
|
||||
Active int64 `json:"active"`
|
||||
AverageSteps float64 `json:"averageSteps"`
|
||||
AverageTimeMs int64 `json:"averageTimeMs"`
|
||||
CompletionRate float64 `json:"completionRate"`
|
||||
}
|
||||
|
||||
type JourneyActionStat struct {
|
||||
Category string `json:"category"`
|
||||
Action string `json:"action"`
|
||||
Events int64 `json:"events"`
|
||||
Journeys int64 `json:"journeys"`
|
||||
}
|
||||
|
||||
// Event kinds. Impressions say a row was drawn; focus says the remote actually landed
|
||||
// on it and for how long; select says something was opened from it.
|
||||
const (
|
||||
@@ -231,10 +253,64 @@ func (s *Store) AnalyticsUsers(ctx context.Context, since time.Time) ([]Analytic
|
||||
return out, rows.Err()
|
||||
}
|
||||
|
||||
func (s *Store) JourneyStats(ctx context.Context, userID string, since time.Time) (JourneyStats, error) {
|
||||
var value JourneyStats
|
||||
err := s.pool.QueryRow(ctx, `
|
||||
WITH visits AS (
|
||||
SELECT emby_user_id, journey_id, count(*) AS steps,
|
||||
min(occurred_at) AS started_at, max(occurred_at) AS last_at,
|
||||
bool_or(action = 'journey_end') AS completed
|
||||
FROM journey_events
|
||||
WHERE occurred_at >= $1 AND ($2 = '' OR emby_user_id = $2)
|
||||
GROUP BY emby_user_id, journey_id
|
||||
)
|
||||
SELECT coalesce(sum(steps), 0), count(*), count(DISTINCT emby_user_id),
|
||||
count(*) FILTER (WHERE completed),
|
||||
count(*) FILTER (WHERE NOT completed AND last_at < now() - interval '30 minutes'),
|
||||
count(*) FILTER (WHERE NOT completed AND last_at >= now() - interval '30 minutes'),
|
||||
coalesce(avg(steps), 0)::double precision,
|
||||
round(coalesce(avg(extract(epoch FROM (last_at - started_at)) * 1000)
|
||||
FILTER (WHERE completed), 0))::bigint
|
||||
FROM visits`, since, userID).Scan(
|
||||
&value.Events, &value.Journeys, &value.Viewers, &value.Completed,
|
||||
&value.Abandoned, &value.Active, &value.AverageSteps, &value.AverageTimeMs,
|
||||
)
|
||||
if err != nil {
|
||||
return JourneyStats{}, fmt.Errorf("store: journey stats: %w", err)
|
||||
}
|
||||
finished := value.Completed + value.Abandoned
|
||||
if finished > 0 {
|
||||
value.CompletionRate = float64(value.Completed) / float64(finished)
|
||||
}
|
||||
return value, nil
|
||||
}
|
||||
|
||||
func (s *Store) JourneyActionStats(ctx context.Context, userID string, since time.Time) ([]JourneyActionStat, error) {
|
||||
rows, err := s.pool.Query(ctx, `
|
||||
SELECT category, action, count(*), count(DISTINCT journey_id)
|
||||
FROM journey_events
|
||||
WHERE occurred_at >= $1 AND ($2 = '' OR emby_user_id = $2)
|
||||
AND action NOT IN ('journey_start', 'journey_end')
|
||||
GROUP BY category, action ORDER BY count(*) DESC, category, action`, since, userID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("store: journey action stats: %w", err)
|
||||
}
|
||||
defer rows.Close()
|
||||
out := []JourneyActionStat{}
|
||||
for rows.Next() {
|
||||
var value JourneyActionStat
|
||||
if err := rows.Scan(&value.Category, &value.Action, &value.Events, &value.Journeys); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
out = append(out, value)
|
||||
}
|
||||
return out, rows.Err()
|
||||
}
|
||||
|
||||
func (s *Store) UserFeatureStats(ctx context.Context, userID string, since time.Time) ([]FeatureStat, error) {
|
||||
rows, err := s.pool.Query(ctx, `
|
||||
SELECT feature, count(*), max(occurred_at) FROM journey_events
|
||||
WHERE emby_user_id=$1 AND occurred_at >= $2 AND feature <> ''
|
||||
WHERE ($1 = '' OR emby_user_id=$1) AND occurred_at >= $2 AND feature <> ''
|
||||
AND action NOT IN ('screen_view', 'journey_start', 'journey_end')
|
||||
GROUP BY feature ORDER BY count(*) DESC, feature`, userID, since)
|
||||
if err != nil {
|
||||
@@ -259,7 +335,7 @@ func (s *Store) UserPaths(ctx context.Context, userID string, since time.Time) (
|
||||
coalesce(nullif(target,''), nullif(screen,''), feature) AS node,
|
||||
lag(coalesce(nullif(target,''), nullif(screen,''), feature)) OVER
|
||||
(PARTITION BY journey_id ORDER BY sequence, occurred_at, id) AS previous
|
||||
FROM journey_events WHERE emby_user_id=$1 AND occurred_at >= $2
|
||||
FROM journey_events WHERE ($1 = '' OR emby_user_id=$1) AND occurred_at >= $2
|
||||
), path_steps AS (
|
||||
SELECT previous AS from_node, node AS to_node FROM ordered
|
||||
WHERE previous IS NOT NULL AND node IS NOT NULL AND previous <> node
|
||||
|
||||
Reference in New Issue
Block a user