mirror of
https://github.com/gabehf/Koito.git
synced 2026-04-22 12:01:52 -07:00
maybe fixed for total listen activity
This commit is contained in:
parent
2925425750
commit
913c6c00e5
6 changed files with 95 additions and 47 deletions
2
Makefile
2
Makefile
|
|
@ -10,7 +10,7 @@ postgres.schemadump:
|
||||||
-v --dbname="koitodb" -f "/tmp/dump/schema.sql"
|
-v --dbname="koitodb" -f "/tmp/dump/schema.sql"
|
||||||
|
|
||||||
postgres.run:
|
postgres.run:
|
||||||
docker run --name koito-db -p 5432:5432 -e POSTGRES_PASSWORD=secret -d postgres
|
docker run --name koito-db -p 5432:5432 -e POSTGRES_PASSWORD=secret -e TZ=America/New_York -d postgres
|
||||||
|
|
||||||
postgres.run-scratch:
|
postgres.run-scratch:
|
||||||
docker run --name koito-scratch -p 5433:5432 -e POSTGRES_PASSWORD=secret -d postgres
|
docker run --name koito-scratch -p 5433:5432 -e POSTGRES_PASSWORD=secret -d postgres
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ VALUES ($1, $2, $3, $4)
|
||||||
ON CONFLICT DO NOTHING;
|
ON CONFLICT DO NOTHING;
|
||||||
|
|
||||||
-- name: GetLastListensPaginated :many
|
-- name: GetLastListensPaginated :many
|
||||||
SELECT
|
SELECT
|
||||||
l.*,
|
l.*,
|
||||||
t.title AS track_title,
|
t.title AS track_title,
|
||||||
t.release_id AS release_id,
|
t.release_id AS release_id,
|
||||||
|
|
@ -16,31 +16,31 @@ ORDER BY l.listened_at DESC
|
||||||
LIMIT $3 OFFSET $4;
|
LIMIT $3 OFFSET $4;
|
||||||
|
|
||||||
-- name: GetLastListensFromArtistPaginated :many
|
-- name: GetLastListensFromArtistPaginated :many
|
||||||
SELECT
|
SELECT
|
||||||
l.*,
|
l.*,
|
||||||
t.title AS track_title,
|
t.title AS track_title,
|
||||||
t.release_id AS release_id,
|
t.release_id AS release_id,
|
||||||
get_artists_for_track(t.id) AS artists
|
get_artists_for_track(t.id) AS artists
|
||||||
FROM listens l
|
FROM listens l
|
||||||
JOIN tracks_with_title t ON l.track_id = t.id
|
JOIN tracks_with_title t ON l.track_id = t.id
|
||||||
JOIN artist_tracks at ON t.id = at.track_id
|
JOIN artist_tracks at ON t.id = at.track_id
|
||||||
WHERE at.artist_id = $5
|
WHERE at.artist_id = $5
|
||||||
AND l.listened_at BETWEEN $1 AND $2
|
AND l.listened_at BETWEEN $1 AND $2
|
||||||
ORDER BY l.listened_at DESC
|
ORDER BY l.listened_at DESC
|
||||||
LIMIT $3 OFFSET $4;
|
LIMIT $3 OFFSET $4;
|
||||||
|
|
||||||
-- name: GetFirstListenFromArtist :one
|
-- name: GetFirstListenFromArtist :one
|
||||||
SELECT
|
SELECT
|
||||||
l.*
|
l.*
|
||||||
FROM listens l
|
FROM listens l
|
||||||
JOIN tracks_with_title t ON l.track_id = t.id
|
JOIN tracks_with_title t ON l.track_id = t.id
|
||||||
JOIN artist_tracks at ON t.id = at.track_id
|
JOIN artist_tracks at ON t.id = at.track_id
|
||||||
WHERE at.artist_id = $1
|
WHERE at.artist_id = $1
|
||||||
ORDER BY l.listened_at ASC
|
ORDER BY l.listened_at ASC
|
||||||
LIMIT 1;
|
LIMIT 1;
|
||||||
|
|
||||||
-- name: GetLastListensFromReleasePaginated :many
|
-- name: GetLastListensFromReleasePaginated :many
|
||||||
SELECT
|
SELECT
|
||||||
l.*,
|
l.*,
|
||||||
t.title AS track_title,
|
t.title AS track_title,
|
||||||
t.release_id AS release_id,
|
t.release_id AS release_id,
|
||||||
|
|
@ -53,7 +53,7 @@ ORDER BY l.listened_at DESC
|
||||||
LIMIT $3 OFFSET $4;
|
LIMIT $3 OFFSET $4;
|
||||||
|
|
||||||
-- name: GetFirstListenFromRelease :one
|
-- name: GetFirstListenFromRelease :one
|
||||||
SELECT
|
SELECT
|
||||||
l.*
|
l.*
|
||||||
FROM listens l
|
FROM listens l
|
||||||
JOIN tracks t ON l.track_id = t.id
|
JOIN tracks t ON l.track_id = t.id
|
||||||
|
|
@ -62,7 +62,7 @@ ORDER BY l.listened_at ASC
|
||||||
LIMIT 1;
|
LIMIT 1;
|
||||||
|
|
||||||
-- name: GetLastListensFromTrackPaginated :many
|
-- name: GetLastListensFromTrackPaginated :many
|
||||||
SELECT
|
SELECT
|
||||||
l.*,
|
l.*,
|
||||||
t.title AS track_title,
|
t.title AS track_title,
|
||||||
t.release_id AS release_id,
|
t.release_id AS release_id,
|
||||||
|
|
@ -75,7 +75,7 @@ ORDER BY l.listened_at DESC
|
||||||
LIMIT $3 OFFSET $4;
|
LIMIT $3 OFFSET $4;
|
||||||
|
|
||||||
-- name: GetFirstListenFromTrack :one
|
-- name: GetFirstListenFromTrack :one
|
||||||
SELECT
|
SELECT
|
||||||
l.*
|
l.*
|
||||||
FROM listens l
|
FROM listens l
|
||||||
JOIN tracks t ON l.track_id = t.id
|
JOIN tracks t ON l.track_id = t.id
|
||||||
|
|
@ -83,6 +83,13 @@ WHERE t.id = $1
|
||||||
ORDER BY l.listened_at ASC
|
ORDER BY l.listened_at ASC
|
||||||
LIMIT 1;
|
LIMIT 1;
|
||||||
|
|
||||||
|
-- name: GetFirstListen :one
|
||||||
|
SELECT
|
||||||
|
*
|
||||||
|
FROM listens
|
||||||
|
ORDER BY listened_at ASC
|
||||||
|
LIMIT 1;
|
||||||
|
|
||||||
-- name: CountListens :one
|
-- name: CountListens :one
|
||||||
SELECT COUNT(*) AS total_count
|
SELECT COUNT(*) AS total_count
|
||||||
FROM listens l
|
FROM listens l
|
||||||
|
|
@ -138,20 +145,27 @@ WHERE l.listened_at BETWEEN $1 AND $2
|
||||||
|
|
||||||
-- name: ListenActivity :many
|
-- name: ListenActivity :many
|
||||||
WITH buckets AS (
|
WITH buckets AS (
|
||||||
SELECT generate_series($1::timestamptz, $2::timestamptz, $3::interval) AS bucket_start
|
SELECT
|
||||||
|
d::date AS bucket_start_date,
|
||||||
|
(d + $3::interval)::date AS bucket_end_date
|
||||||
|
FROM generate_series(
|
||||||
|
$1::date,
|
||||||
|
$2::date,
|
||||||
|
$3::interval
|
||||||
|
) AS d
|
||||||
),
|
),
|
||||||
bucketed_listens AS (
|
bucketed_listens AS (
|
||||||
SELECT
|
SELECT
|
||||||
b.bucket_start,
|
b.bucket_start_date::timestamptz,
|
||||||
COUNT(l.listened_at) AS listen_count
|
COUNT(l.listened_at) AS listen_count
|
||||||
FROM buckets b
|
FROM buckets b
|
||||||
LEFT JOIN listens l
|
LEFT JOIN listens l
|
||||||
ON l.listened_at >= b.bucket_start
|
ON l.listened_at >= b.bucket_start_date::timestamptz
|
||||||
AND l.listened_at < b.bucket_start + $3::interval
|
AND l.listened_at < b.bucket_end_date::timestamptz
|
||||||
GROUP BY b.bucket_start
|
GROUP BY b.bucket_start_date
|
||||||
ORDER BY b.bucket_start
|
ORDER BY b.bucket_start_date
|
||||||
)
|
)
|
||||||
SELECT * FROM bucketed_listens;
|
SELECT bucketed_listens.bucket_start_date::timestamptz, listen_count FROM bucketed_listens;
|
||||||
|
|
||||||
-- name: ListenActivityForArtist :many
|
-- name: ListenActivityForArtist :many
|
||||||
WITH buckets AS (
|
WITH buckets AS (
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@ func GetListenActivityHandler(store db.DB) func(w http.ResponseWriter, r *http.R
|
||||||
|
|
||||||
rangeStr := r.URL.Query().Get("range")
|
rangeStr := r.URL.Query().Get("range")
|
||||||
_range, err := strconv.Atoi(rangeStr)
|
_range, err := strconv.Atoi(rangeStr)
|
||||||
if err != nil {
|
if err != nil && rangeStr != "" {
|
||||||
l.Debug().AnErr("error", err).Msg("GetListenActivityHandler: Invalid range parameter")
|
l.Debug().AnErr("error", err).Msg("GetListenActivityHandler: Invalid range parameter")
|
||||||
utils.WriteError(w, "invalid range parameter", http.StatusBadRequest)
|
utils.WriteError(w, "invalid range parameter", http.StatusBadRequest)
|
||||||
return
|
return
|
||||||
|
|
@ -27,7 +27,7 @@ func GetListenActivityHandler(store db.DB) func(w http.ResponseWriter, r *http.R
|
||||||
|
|
||||||
monthStr := r.URL.Query().Get("month")
|
monthStr := r.URL.Query().Get("month")
|
||||||
month, err := strconv.Atoi(monthStr)
|
month, err := strconv.Atoi(monthStr)
|
||||||
if err != nil {
|
if err != nil && monthStr != "" {
|
||||||
l.Debug().AnErr("error", err).Msg("GetListenActivityHandler: Invalid month parameter")
|
l.Debug().AnErr("error", err).Msg("GetListenActivityHandler: Invalid month parameter")
|
||||||
utils.WriteError(w, "invalid month parameter", http.StatusBadRequest)
|
utils.WriteError(w, "invalid month parameter", http.StatusBadRequest)
|
||||||
return
|
return
|
||||||
|
|
@ -35,7 +35,7 @@ func GetListenActivityHandler(store db.DB) func(w http.ResponseWriter, r *http.R
|
||||||
|
|
||||||
yearStr := r.URL.Query().Get("year")
|
yearStr := r.URL.Query().Get("year")
|
||||||
year, err := strconv.Atoi(yearStr)
|
year, err := strconv.Atoi(yearStr)
|
||||||
if err != nil {
|
if err != nil && yearStr != "" {
|
||||||
l.Debug().AnErr("error", err).Msg("GetListenActivityHandler: Invalid year parameter")
|
l.Debug().AnErr("error", err).Msg("GetListenActivityHandler: Invalid year parameter")
|
||||||
utils.WriteError(w, "invalid year parameter", http.StatusBadRequest)
|
utils.WriteError(w, "invalid year parameter", http.StatusBadRequest)
|
||||||
return
|
return
|
||||||
|
|
@ -43,7 +43,7 @@ func GetListenActivityHandler(store db.DB) func(w http.ResponseWriter, r *http.R
|
||||||
|
|
||||||
artistIdStr := r.URL.Query().Get("artist_id")
|
artistIdStr := r.URL.Query().Get("artist_id")
|
||||||
artistId, err := strconv.Atoi(artistIdStr)
|
artistId, err := strconv.Atoi(artistIdStr)
|
||||||
if err != nil {
|
if err != nil && artistIdStr != "" {
|
||||||
l.Debug().AnErr("error", err).Msg("GetListenActivityHandler: Invalid artist ID parameter")
|
l.Debug().AnErr("error", err).Msg("GetListenActivityHandler: Invalid artist ID parameter")
|
||||||
utils.WriteError(w, "invalid artist ID parameter", http.StatusBadRequest)
|
utils.WriteError(w, "invalid artist ID parameter", http.StatusBadRequest)
|
||||||
return
|
return
|
||||||
|
|
@ -51,7 +51,7 @@ func GetListenActivityHandler(store db.DB) func(w http.ResponseWriter, r *http.R
|
||||||
|
|
||||||
albumIdStr := r.URL.Query().Get("album_id")
|
albumIdStr := r.URL.Query().Get("album_id")
|
||||||
albumId, err := strconv.Atoi(albumIdStr)
|
albumId, err := strconv.Atoi(albumIdStr)
|
||||||
if err != nil {
|
if err != nil && albumIdStr != "" {
|
||||||
l.Debug().AnErr("error", err).Msg("GetListenActivityHandler: Invalid album ID parameter")
|
l.Debug().AnErr("error", err).Msg("GetListenActivityHandler: Invalid album ID parameter")
|
||||||
utils.WriteError(w, "invalid album ID parameter", http.StatusBadRequest)
|
utils.WriteError(w, "invalid album ID parameter", http.StatusBadRequest)
|
||||||
return
|
return
|
||||||
|
|
@ -59,7 +59,7 @@ func GetListenActivityHandler(store db.DB) func(w http.ResponseWriter, r *http.R
|
||||||
|
|
||||||
trackIdStr := r.URL.Query().Get("track_id")
|
trackIdStr := r.URL.Query().Get("track_id")
|
||||||
trackId, err := strconv.Atoi(trackIdStr)
|
trackId, err := strconv.Atoi(trackIdStr)
|
||||||
if err != nil {
|
if err != nil && trackIdStr != "" {
|
||||||
l.Debug().AnErr("error", err).Msg("GetListenActivityHandler: Invalid track ID parameter")
|
l.Debug().AnErr("error", err).Msg("GetListenActivityHandler: Invalid track ID parameter")
|
||||||
utils.WriteError(w, "invalid track ID parameter", http.StatusBadRequest)
|
utils.WriteError(w, "invalid track ID parameter", http.StatusBadRequest)
|
||||||
return
|
return
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ import (
|
||||||
"github.com/gabehf/koito/internal/db"
|
"github.com/gabehf/koito/internal/db"
|
||||||
"github.com/gabehf/koito/internal/logger"
|
"github.com/gabehf/koito/internal/logger"
|
||||||
"github.com/gabehf/koito/internal/repository"
|
"github.com/gabehf/koito/internal/repository"
|
||||||
|
"github.com/jackc/pgx/v5/pgtype"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (d *Psql) GetListenActivity(ctx context.Context, opts db.ListenActivityOpts) ([]db.ListenActivityItem, error) {
|
func (d *Psql) GetListenActivity(ctx context.Context, opts db.ListenActivityOpts) ([]db.ListenActivityItem, error) {
|
||||||
|
|
@ -88,8 +89,8 @@ func (d *Psql) GetListenActivity(ctx context.Context, opts db.ListenActivityOpts
|
||||||
l.Debug().Msgf("Fetching listen activity for %d %s(s) from %v to %v",
|
l.Debug().Msgf("Fetching listen activity for %d %s(s) from %v to %v",
|
||||||
opts.Range, opts.Step, t1.Format("Jan 02, 2006 15:04:05"), t2.Format("Jan 02, 2006 15:04:05"))
|
opts.Range, opts.Step, t1.Format("Jan 02, 2006 15:04:05"), t2.Format("Jan 02, 2006 15:04:05"))
|
||||||
rows, err := d.q.ListenActivity(ctx, repository.ListenActivityParams{
|
rows, err := d.q.ListenActivity(ctx, repository.ListenActivityParams{
|
||||||
Column1: t1,
|
Column1: pgtype.Date{Time: t1, Valid: true},
|
||||||
Column2: t2,
|
Column2: pgtype.Date{Time: t2, Valid: true},
|
||||||
Column3: stepToInterval(opts.Step),
|
Column3: stepToInterval(opts.Step),
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -98,7 +99,7 @@ func (d *Psql) GetListenActivity(ctx context.Context, opts db.ListenActivityOpts
|
||||||
listenActivity = make([]db.ListenActivityItem, len(rows))
|
listenActivity = make([]db.ListenActivityItem, len(rows))
|
||||||
for i, row := range rows {
|
for i, row := range rows {
|
||||||
t := db.ListenActivityItem{
|
t := db.ListenActivityItem{
|
||||||
Start: row.BucketStart,
|
Start: row.BucketedListensBucketStartDate,
|
||||||
Listens: row.ListenCount,
|
Listens: row.ListenCount,
|
||||||
}
|
}
|
||||||
listenActivity[i] = t
|
listenActivity[i] = t
|
||||||
|
|
|
||||||
|
|
@ -190,12 +190,32 @@ func (q *Queries) DeleteListen(ctx context.Context, arg DeleteListenParams) erro
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const getFirstListen = `-- name: GetFirstListen :one
|
||||||
|
SELECT
|
||||||
|
track_id, listened_at, client, user_id
|
||||||
|
FROM listens
|
||||||
|
ORDER BY listened_at ASC
|
||||||
|
LIMIT 1
|
||||||
|
`
|
||||||
|
|
||||||
|
func (q *Queries) GetFirstListen(ctx context.Context) (Listen, error) {
|
||||||
|
row := q.db.QueryRow(ctx, getFirstListen)
|
||||||
|
var i Listen
|
||||||
|
err := row.Scan(
|
||||||
|
&i.TrackID,
|
||||||
|
&i.ListenedAt,
|
||||||
|
&i.Client,
|
||||||
|
&i.UserID,
|
||||||
|
)
|
||||||
|
return i, err
|
||||||
|
}
|
||||||
|
|
||||||
const getFirstListenFromArtist = `-- name: GetFirstListenFromArtist :one
|
const getFirstListenFromArtist = `-- name: GetFirstListenFromArtist :one
|
||||||
SELECT
|
SELECT
|
||||||
l.track_id, l.listened_at, l.client, l.user_id
|
l.track_id, l.listened_at, l.client, l.user_id
|
||||||
FROM listens l
|
FROM listens l
|
||||||
JOIN tracks_with_title t ON l.track_id = t.id
|
JOIN tracks_with_title t ON l.track_id = t.id
|
||||||
JOIN artist_tracks at ON t.id = at.track_id
|
JOIN artist_tracks at ON t.id = at.track_id
|
||||||
WHERE at.artist_id = $1
|
WHERE at.artist_id = $1
|
||||||
ORDER BY l.listened_at ASC
|
ORDER BY l.listened_at ASC
|
||||||
LIMIT 1
|
LIMIT 1
|
||||||
|
|
@ -214,7 +234,7 @@ func (q *Queries) GetFirstListenFromArtist(ctx context.Context, artistID int32)
|
||||||
}
|
}
|
||||||
|
|
||||||
const getFirstListenFromRelease = `-- name: GetFirstListenFromRelease :one
|
const getFirstListenFromRelease = `-- name: GetFirstListenFromRelease :one
|
||||||
SELECT
|
SELECT
|
||||||
l.track_id, l.listened_at, l.client, l.user_id
|
l.track_id, l.listened_at, l.client, l.user_id
|
||||||
FROM listens l
|
FROM listens l
|
||||||
JOIN tracks t ON l.track_id = t.id
|
JOIN tracks t ON l.track_id = t.id
|
||||||
|
|
@ -236,7 +256,7 @@ func (q *Queries) GetFirstListenFromRelease(ctx context.Context, releaseID int32
|
||||||
}
|
}
|
||||||
|
|
||||||
const getFirstListenFromTrack = `-- name: GetFirstListenFromTrack :one
|
const getFirstListenFromTrack = `-- name: GetFirstListenFromTrack :one
|
||||||
SELECT
|
SELECT
|
||||||
l.track_id, l.listened_at, l.client, l.user_id
|
l.track_id, l.listened_at, l.client, l.user_id
|
||||||
FROM listens l
|
FROM listens l
|
||||||
JOIN tracks t ON l.track_id = t.id
|
JOIN tracks t ON l.track_id = t.id
|
||||||
|
|
@ -258,14 +278,14 @@ func (q *Queries) GetFirstListenFromTrack(ctx context.Context, id int32) (Listen
|
||||||
}
|
}
|
||||||
|
|
||||||
const getLastListensFromArtistPaginated = `-- name: GetLastListensFromArtistPaginated :many
|
const getLastListensFromArtistPaginated = `-- name: GetLastListensFromArtistPaginated :many
|
||||||
SELECT
|
SELECT
|
||||||
l.track_id, l.listened_at, l.client, l.user_id,
|
l.track_id, l.listened_at, l.client, l.user_id,
|
||||||
t.title AS track_title,
|
t.title AS track_title,
|
||||||
t.release_id AS release_id,
|
t.release_id AS release_id,
|
||||||
get_artists_for_track(t.id) AS artists
|
get_artists_for_track(t.id) AS artists
|
||||||
FROM listens l
|
FROM listens l
|
||||||
JOIN tracks_with_title t ON l.track_id = t.id
|
JOIN tracks_with_title t ON l.track_id = t.id
|
||||||
JOIN artist_tracks at ON t.id = at.track_id
|
JOIN artist_tracks at ON t.id = at.track_id
|
||||||
WHERE at.artist_id = $5
|
WHERE at.artist_id = $5
|
||||||
AND l.listened_at BETWEEN $1 AND $2
|
AND l.listened_at BETWEEN $1 AND $2
|
||||||
ORDER BY l.listened_at DESC
|
ORDER BY l.listened_at DESC
|
||||||
|
|
@ -325,7 +345,7 @@ func (q *Queries) GetLastListensFromArtistPaginated(ctx context.Context, arg Get
|
||||||
}
|
}
|
||||||
|
|
||||||
const getLastListensFromReleasePaginated = `-- name: GetLastListensFromReleasePaginated :many
|
const getLastListensFromReleasePaginated = `-- name: GetLastListensFromReleasePaginated :many
|
||||||
SELECT
|
SELECT
|
||||||
l.track_id, l.listened_at, l.client, l.user_id,
|
l.track_id, l.listened_at, l.client, l.user_id,
|
||||||
t.title AS track_title,
|
t.title AS track_title,
|
||||||
t.release_id AS release_id,
|
t.release_id AS release_id,
|
||||||
|
|
@ -391,7 +411,7 @@ func (q *Queries) GetLastListensFromReleasePaginated(ctx context.Context, arg Ge
|
||||||
}
|
}
|
||||||
|
|
||||||
const getLastListensFromTrackPaginated = `-- name: GetLastListensFromTrackPaginated :many
|
const getLastListensFromTrackPaginated = `-- name: GetLastListensFromTrackPaginated :many
|
||||||
SELECT
|
SELECT
|
||||||
l.track_id, l.listened_at, l.client, l.user_id,
|
l.track_id, l.listened_at, l.client, l.user_id,
|
||||||
t.title AS track_title,
|
t.title AS track_title,
|
||||||
t.release_id AS release_id,
|
t.release_id AS release_id,
|
||||||
|
|
@ -457,7 +477,7 @@ func (q *Queries) GetLastListensFromTrackPaginated(ctx context.Context, arg GetL
|
||||||
}
|
}
|
||||||
|
|
||||||
const getLastListensPaginated = `-- name: GetLastListensPaginated :many
|
const getLastListensPaginated = `-- name: GetLastListensPaginated :many
|
||||||
SELECT
|
SELECT
|
||||||
l.track_id, l.listened_at, l.client, l.user_id,
|
l.track_id, l.listened_at, l.client, l.user_id,
|
||||||
t.title AS track_title,
|
t.title AS track_title,
|
||||||
t.release_id AS release_id,
|
t.release_id AS release_id,
|
||||||
|
|
@ -676,31 +696,38 @@ func (q *Queries) InsertListen(ctx context.Context, arg InsertListenParams) erro
|
||||||
|
|
||||||
const listenActivity = `-- name: ListenActivity :many
|
const listenActivity = `-- name: ListenActivity :many
|
||||||
WITH buckets AS (
|
WITH buckets AS (
|
||||||
SELECT generate_series($1::timestamptz, $2::timestamptz, $3::interval) AS bucket_start
|
SELECT
|
||||||
|
d::date AS bucket_start_date,
|
||||||
|
(d + $3::interval)::date AS bucket_end_date
|
||||||
|
FROM generate_series(
|
||||||
|
$1::date,
|
||||||
|
$2::date,
|
||||||
|
$3::interval
|
||||||
|
) AS d
|
||||||
),
|
),
|
||||||
bucketed_listens AS (
|
bucketed_listens AS (
|
||||||
SELECT
|
SELECT
|
||||||
b.bucket_start,
|
b.bucket_start_date::timestamptz,
|
||||||
COUNT(l.listened_at) AS listen_count
|
COUNT(l.listened_at) AS listen_count
|
||||||
FROM buckets b
|
FROM buckets b
|
||||||
LEFT JOIN listens l
|
LEFT JOIN listens l
|
||||||
ON l.listened_at >= b.bucket_start
|
ON l.listened_at >= b.bucket_start_date::timestamptz
|
||||||
AND l.listened_at < b.bucket_start + $3::interval
|
AND l.listened_at < b.bucket_end_date::timestamptz
|
||||||
GROUP BY b.bucket_start
|
GROUP BY b.bucket_start_date
|
||||||
ORDER BY b.bucket_start
|
ORDER BY b.bucket_start_date
|
||||||
)
|
)
|
||||||
SELECT bucket_start, listen_count FROM bucketed_listens
|
SELECT bucketed_listens.bucket_start_date::timestamptz, listen_count FROM bucketed_listens
|
||||||
`
|
`
|
||||||
|
|
||||||
type ListenActivityParams struct {
|
type ListenActivityParams struct {
|
||||||
Column1 time.Time
|
Column1 pgtype.Date
|
||||||
Column2 time.Time
|
Column2 pgtype.Date
|
||||||
Column3 pgtype.Interval
|
Column3 pgtype.Interval
|
||||||
}
|
}
|
||||||
|
|
||||||
type ListenActivityRow struct {
|
type ListenActivityRow struct {
|
||||||
BucketStart time.Time
|
BucketedListensBucketStartDate time.Time
|
||||||
ListenCount int64
|
ListenCount int64
|
||||||
}
|
}
|
||||||
|
|
||||||
func (q *Queries) ListenActivity(ctx context.Context, arg ListenActivityParams) ([]ListenActivityRow, error) {
|
func (q *Queries) ListenActivity(ctx context.Context, arg ListenActivityParams) ([]ListenActivityRow, error) {
|
||||||
|
|
@ -712,7 +739,7 @@ func (q *Queries) ListenActivity(ctx context.Context, arg ListenActivityParams)
|
||||||
var items []ListenActivityRow
|
var items []ListenActivityRow
|
||||||
for rows.Next() {
|
for rows.Next() {
|
||||||
var i ListenActivityRow
|
var i ListenActivityRow
|
||||||
if err := rows.Scan(&i.BucketStart, &i.ListenCount); err != nil {
|
if err := rows.Scan(&i.BucketedListensBucketStartDate, &i.ListenCount); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
items = append(items, i)
|
items = append(items, i)
|
||||||
|
|
|
||||||
|
|
@ -127,6 +127,12 @@ func DateRange(week, month, year int) (time.Time, time.Time, error) {
|
||||||
return start, end, nil
|
return start, end, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Returns a time.Time that represents the first moment of the day of t.
|
||||||
|
func BeginningOfDay(t time.Time) time.Time {
|
||||||
|
year, month, day := t.Date()
|
||||||
|
return time.Date(year, month, day, 0, 0, 0, 0, t.Location())
|
||||||
|
}
|
||||||
|
|
||||||
// CopyFile copies a file from src to dst. If src and dst files exist, and are
|
// CopyFile copies a file from src to dst. If src and dst files exist, and are
|
||||||
// the same, then return success. Otherise, attempt to create a hard link
|
// the same, then return success. Otherise, attempt to create a hard link
|
||||||
// between the two files. If that fail, copy the file contents from src to dst.
|
// between the two files. If that fail, copy the file contents from src to dst.
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue