0.1.38 gateway
This commit is contained in:
@@ -119,6 +119,101 @@ type JourneyActionStat struct {
|
||||
Journeys int64 `json:"journeys"`
|
||||
}
|
||||
|
||||
// ViewStats is app use measured at the launcher, rather than media playback. A visit is a
|
||||
// foreground journey that reached home; a viewer is one signed-in profile in that window.
|
||||
type ViewStats struct {
|
||||
Visits int64 `json:"visits"`
|
||||
Viewers int64 `json:"viewers"`
|
||||
}
|
||||
|
||||
type ViewBucket struct {
|
||||
Label string `json:"label"`
|
||||
Visits int64 `json:"visits"`
|
||||
Viewers int64 `json:"viewers"`
|
||||
}
|
||||
|
||||
type ViewsReport struct {
|
||||
Today ViewStats `json:"today"`
|
||||
LastWeek ViewStats `json:"lastWeek"`
|
||||
Daily []ViewBucket `json:"daily"`
|
||||
Hourly []ViewBucket `json:"hourly"`
|
||||
BusiestHour string `json:"busiestHour"`
|
||||
}
|
||||
|
||||
// ViewsReport aggregates only app launcher openings. `journey_start` remains in the
|
||||
// predicate so deployments gain a useful history before clients begin sending home_open.
|
||||
func (s *Store) ViewsReport(ctx context.Context, now time.Time) (ViewsReport, error) {
|
||||
var out ViewsReport
|
||||
location, err := time.LoadLocation("Pacific/Auckland")
|
||||
if err != nil {
|
||||
location = time.UTC
|
||||
}
|
||||
localNow := now.In(location)
|
||||
todayStart := time.Date(localNow.Year(), localNow.Month(), localNow.Day(), 0, 0, 0, 0, location).UTC()
|
||||
// The dashboard comparison ends at the same elapsed point in last week's day, not at
|
||||
// midnight, so a morning view is compared with another morning rather than a full day.
|
||||
lastWeekStart := todayStart.AddDate(0, 0, -7)
|
||||
lastWeekEnd := lastWeekStart.Add(now.Sub(todayStart))
|
||||
count := func(start, end time.Time) (ViewStats, error) {
|
||||
var value ViewStats
|
||||
err := s.pool.QueryRow(ctx, `SELECT count(DISTINCT journey_id), count(DISTINCT emby_user_id)
|
||||
FROM journey_events WHERE occurred_at >= $1 AND occurred_at < $2
|
||||
AND action IN ('home_open', 'journey_start')`, start, end).Scan(&value.Visits, &value.Viewers)
|
||||
return value, err
|
||||
}
|
||||
if out.Today, err = count(todayStart, now); err != nil {
|
||||
return out, fmt.Errorf("store: today views: %w", err)
|
||||
}
|
||||
if out.LastWeek, err = count(lastWeekStart, lastWeekEnd); err != nil {
|
||||
return out, fmt.Errorf("store: last-week views: %w", err)
|
||||
}
|
||||
rows, err := s.pool.Query(ctx, `SELECT to_char(timezone('Pacific/Auckland', occurred_at), 'YYYY-MM-DD'),
|
||||
count(DISTINCT journey_id), count(DISTINCT emby_user_id)
|
||||
FROM journey_events WHERE occurred_at >= $1 AND action IN ('home_open', 'journey_start')
|
||||
GROUP BY 1 ORDER BY 1`, todayStart.AddDate(0, 0, -29))
|
||||
if err != nil {
|
||||
return out, fmt.Errorf("store: daily views: %w", err)
|
||||
}
|
||||
for rows.Next() {
|
||||
var bucket ViewBucket
|
||||
if err := rows.Scan(&bucket.Label, &bucket.Visits, &bucket.Viewers); err != nil {
|
||||
rows.Close()
|
||||
return out, err
|
||||
}
|
||||
out.Daily = append(out.Daily, bucket)
|
||||
}
|
||||
if err := rows.Err(); err != nil {
|
||||
rows.Close()
|
||||
return out, err
|
||||
}
|
||||
rows.Close()
|
||||
rows, err = s.pool.Query(ctx, `SELECT to_char(timezone('Pacific/Auckland', occurred_at), 'HH24:00'),
|
||||
count(DISTINCT journey_id), count(DISTINCT emby_user_id)
|
||||
FROM journey_events WHERE occurred_at >= $1 AND occurred_at < $2 AND action IN ('home_open', 'journey_start')
|
||||
GROUP BY 1 ORDER BY 1`, todayStart, now)
|
||||
if err != nil {
|
||||
return out, fmt.Errorf("store: hourly views: %w", err)
|
||||
}
|
||||
var busiestVisits int64
|
||||
for rows.Next() {
|
||||
var bucket ViewBucket
|
||||
if err := rows.Scan(&bucket.Label, &bucket.Visits, &bucket.Viewers); err != nil {
|
||||
rows.Close()
|
||||
return out, err
|
||||
}
|
||||
out.Hourly = append(out.Hourly, bucket)
|
||||
if bucket.Visits > busiestVisits {
|
||||
busiestVisits = bucket.Visits
|
||||
out.BusiestHour = bucket.Label
|
||||
}
|
||||
}
|
||||
if err := rows.Err(); err != nil {
|
||||
rows.Close()
|
||||
return out, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// 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 (
|
||||
|
||||
Reference in New Issue
Block a user