diff --git a/src/app/views/albums.zig b/src/app/views/albums.zig index 55db929..17421e7 100644 --- a/src/app/views/albums.zig +++ b/src/app/views/albums.zig @@ -58,8 +58,5 @@ pub fn get(id: []const u8, request: *jetzig.Request) !jetzig.View { const ratings = try queries.entityQueryResult(request, queries.loadQuery(.song, .get_ratings), .{id_int}); try root.put("reviews", ratings); - const peak = try queries.entityQueryResult(request, comptime queries.loadQuery(.album, .peak), .{id_int}); - try root.put("peak", peak); - return request.render(.ok); } diff --git a/src/app/views/albums/get.zmpl b/src/app/views/albums/get.zmpl index e4e2196..6dfd7f7 100644 --- a/src/app/views/albums/get.zmpl +++ b/src/app/views/albums/get.zmpl @@ -18,12 +18,7 @@
- @if ($.album.is_tie) -
{{.album.scrobbles}} scrobbles ({{.album.rank}} place, tied)
- @else
{{.album.scrobbles}} scrobbles ({{.album.rank}} place)
- @end -
All-time peak: {{.peak.rank}} ({{.peak.date}})
{{.album.song_num}} songs
@partial partials/firstlast_listens(firstlast: .firstlast)

Yearly Performance

diff --git a/src/app/views/artists.zig b/src/app/views/artists.zig index e5f13d5..505c3d1 100644 --- a/src/app/views/artists.zig +++ b/src/app/views/artists.zig @@ -57,8 +57,5 @@ pub fn get(id: []const u8, request: *jetzig.Request) !jetzig.View { const timescale = try queries.entityQueryResult(request, queries.loadQuery(.artist, .timescale), .{id_int}); try root.put("yearly", timescale); - const peak = try queries.entityQueryResult(request, comptime queries.loadQuery(.artist, .peak), .{id_int}); - try root.put("peak", peak); - return request.render(.ok); } diff --git a/src/app/views/artists/get.zmpl b/src/app/views/artists/get.zmpl index 9ae4ad3..9f1ff07 100644 --- a/src/app/views/artists/get.zmpl +++ b/src/app/views/artists/get.zmpl @@ -11,12 +11,7 @@ @partial partials/header

{{.artist.artist_name}}

- @if ($.artist.is_tie) -
{{.artist.scrobbles}} scrobbles ({{.artist.rank}} place, tied)
- @else -
{{.artist.scrobbles}} scrobbles ({{.artist.rank}} place)
- @end -
All-time peak: {{.peak.rank}} ({{.peak.date}})
+
{{.artist.scrobbles}} scrobbles ({{.artist.rank}} place)
{{.artist.song_num}} songs
{{.artist.album_num}} albums
diff --git a/src/app/views/songs.zig b/src/app/views/songs.zig index 31876ba..2f1772b 100644 --- a/src/app/views/songs.zig +++ b/src/app/views/songs.zig @@ -45,26 +45,23 @@ pub fn get(id: []const u8, request: *jetzig.Request) !jetzig.View { }; var root = try request.data(.object); - const song = try queries.entityQueryResult(request, comptime queries.loadQuery(.song, .entity_info), .{id_int}); + const song = try queries.entityQueryResult(request, queries.loadQuery(.song, .entity_info), .{id_int}); try root.put("song", song); - const scrobbles = try queries.entityQueryResult(request, comptime queries.loadQuery(.song, .get_scrobbles), .{id_int}); + const scrobbles = try queries.entityQueryResult(request, queries.loadQuery(.song, .get_scrobbles), .{id_int}); try root.put("scrobbles", scrobbles); - const albums = try queries.entityQueryResult(request, comptime queries.loadQuery(.song, .get_albums), .{id_int}); + const albums = try queries.entityQueryResult(request, queries.loadQuery(.song, .get_albums), .{id_int}); try root.put("albums", albums); - const firstlast = try queries.entityQueryResult(request, comptime queries.loadQuery(.song, .firstlast), .{id_int}); + const firstlast = try queries.entityQueryResult(request, queries.loadQuery(.song, .firstlast), .{id_int}); try root.put("firstlast", firstlast); - const timescale = try queries.entityQueryResult(request, comptime queries.loadQuery(.song, .timescale), .{id_int}); + const timescale = try queries.entityQueryResult(request, queries.loadQuery(.song, .timescale), .{id_int}); try root.put("yearly", timescale); - const ratings = try queries.entityQueryResult(request, comptime queries.loadQuery(.song, .get_ratings), .{id_int}); + const ratings = try queries.entityQueryResult(request, queries.loadQuery(.song, .get_ratings), .{id_int}); try root.put("reviews", ratings); - const peak = try queries.entityQueryResult(request, comptime queries.loadQuery(.song, .peak), .{id_int}); - try root.put("peak", peak); - return request.render(.ok); } diff --git a/src/app/views/songs/get.zmpl b/src/app/views/songs/get.zmpl index 2acb7d4..c092ea5 100644 --- a/src/app/views/songs/get.zmpl +++ b/src/app/views/songs/get.zmpl @@ -17,12 +17,7 @@
- @if ($.song.is_tie) -
{{.song.scrobbles}} scrobbles ({{.song.rank}} place, tied)
- @else
{{.song.scrobbles}} scrobbles ({{.song.rank}} place)
- @end -
All-time peak: {{.peak.rank}} ({{.peak.date}})
@partial partials/firstlast_listens(firstlast: .firstlast)

Yearly Performance

@partial partials/timescale(range: .yearly) diff --git a/src/queries.zig b/src/queries.zig index 778e64b..a665a58 100644 --- a/src/queries.zig +++ b/src/queries.zig @@ -3,43 +3,16 @@ const jetzig = @import("jetzig"); const TableRow = @import("types.zig").TableRow; const HyperlinkData = @import("types.zig").HyperlinkData; const std = @import("std"); -const ordinalFmt = @import("./ordinal_fmt.zig").ordinalFmt; -pub fn entityQueryResult(request: *jetzig.Request, comptime query: GeneratedQuery, args: anytype) !*jetzig.Data.Value { +pub fn entityQueryResult(request: *jetzig.Request, query: GeneratedQuery, args: anytype) !*jetzig.Data.Value { //var result = try request.repo.executeSql(query.query, args); // - var Data = jetzig.Data.init(request.allocator); - - if (query.query_type == .peak) { - const id = switch (@TypeOf(args)) { - struct { i64 } => args[0], - else => unreachable, - }; - var result = try request.repo.connection.?.postgresql.connection.queryOpts(query.query, .{}, .{ .allocator = request.allocator, .column_names = true }); - var out: *jetzig.Data.Value = try Data.object(); - var mapper = result.mapper(PeakResult, .{ .dupe = true, .allocator = request.allocator }); - var rank = comptime (std.math.pow(u64, 2, 63) - 1); - var date: []const u8 = undefined; - var scrobbles = std.AutoArrayHashMap(i64, u32).init(request.allocator); - - while (try mapper.next()) |scrobble| { - const res = try scrobbles.getOrPut(scrobble.eid); - if (res.found_existing) res.value_ptr.* += 1 else res.value_ptr.* = 1; - scrobbles.sort(PeakContext{ .keys = scrobbles.keys(), .vals = scrobbles.values(), .preferred = id }); - const idx = scrobbles.getIndex(id); - if (idx != null and idx.? <= rank) { - if (idx.? < rank) rank = idx.?; - date = scrobble.datetime; - } - } - try out.put("rank", ordinalFmt(request.allocator, @as(i64, @bitCast(rank + 1)))); - try out.put("date", date); - return out; - } var result = try request.repo.connection.?.postgresql.connection.queryOpts(query.query, args, .{ .allocator = request.allocator, .column_names = true }); defer result.deinit(); + var Data = jetzig.Data.init(request.allocator); + var artist_list = std.ArrayList(HyperlinkData).init(request.allocator); if (query.query_type == .entity_info) { @@ -78,22 +51,7 @@ pub fn entityQueryResult(request: *jetzig.Request, comptime query: GeneratedQuer } const EntityType = enum { scrobble, song, album, artist }; -const QueryTypeEnum = enum { firstlast, timescale, entities, get_songs, get_albums, get_scrobbles, appears, entity_info, datestreak, entities_by_name, get_ratings, friends, peak }; - -const PeakResult = struct { - eid: i64, - datetime: []const u8, -}; - -const PeakContext = struct { - keys: []i64, - vals: []u32, - preferred: i64, - pub fn lessThan(ctx: @This(), a_index: usize, b_index: usize) bool { - if (ctx.vals[a_index] == ctx.vals[b_index] and ctx.keys[a_index] == ctx.preferred) return true; - return ctx.vals[a_index] > ctx.vals[b_index]; - } -}; +const QueryTypeEnum = enum { firstlast, timescale, entities, get_songs, get_albums, get_scrobbles, appears, entity_info, datestreak, entities_by_name, get_ratings }; const GeneratedQuery = struct { entity: EntityType, @@ -126,10 +84,9 @@ const EntityInfoResult = struct { rank: []const u8, song_num: ?i64 = null, album_num: ?i64 = null, - is_tie: bool, }; -pub fn loadQuery(comptime entity: EntityType, comptime query_type: QueryTypeEnum) GeneratedQuery { +pub fn loadQuery(entity: EntityType, query_type: QueryTypeEnum) GeneratedQuery { return GeneratedQuery{ .entity = entity, .query_type = query_type, @@ -387,49 +344,42 @@ pub fn loadQuery(comptime entity: EntityType, comptime query_type: QueryTypeEnum }, .entity_info => switch (entity) { - .scrobble => @compileError("Cannot specify scrobble for entity_info"), + .scrobble => unreachable, .song => - \\WITH ranked AS ( - \\SELECT songs.name AS song_name, COUNT(songs.id) AS scrobbles, RANK() OVER ( ORDER BY COUNT(songs.id) DESC) AS rank, songs.id AS song_id - \\FROM scrobbles - \\INNER JOIN albumsongs ON albumsongs.id = scrobbles.albumsong - \\INNER JOIN songs ON songs.id = albumsongs.song_id - \\GROUP BY songs.id) - \\SELECT * FROM (SELECT song_name, scrobbles, TO_CHAR(rank,'FM9999th') AS rank, song_id, - \\( rank = lag(rank, 1, -1::bigint) OVER (ORDER BY rank) - \\OR rank = lead(rank, 1, -1::bigint) OVER (ORDER BY rank) - \\)AS is_tie - \\FROM ranked) WHERE song_id = $1; + \\SELECT * FROM + \\(SELECT *, TO_CHAR(ROW_NUMBER() OVER (ORDER BY scrobbles DESC),'FM99999th') AS rank FROM + \\(SELECT songs.name AS song_name, songs.id AS song_id, COUNT(scrobbles) AS scrobbles + \\FROM albumsongs + \\INNER JOIN songs ON albumsongs.song_id = songs.id + \\INNER JOIN scrobbles ON scrobbles.albumsong = albumsongs.id + \\GROUP BY songs.id)) + \\WHERE song_id = $1 , .album => - \\WITH ranked AS ( - \\SELECT albums.name AS album_name, COUNT(albums.id) AS scrobbles, RANK() OVER ( ORDER BY COUNT(albums.id) DESC) AS rank, albums.id AS album_id, COUNT(DISTINCT songs.id) AS song_num - \\FROM scrobbles - \\INNER JOIN albumsongs ON albumsongs.id = scrobbles.albumsong - \\INNER JOIN albums ON albums.id = albumsongs.album_id + \\SELECT album_name, t.album_id, artists.name AS artist_name, artists.id AS artist_id, song_num, scrobbles, rank FROM + \\(SELECT *, TO_CHAR(ROW_NUMBER() OVER (ORDER BY scrobbles DESC),'FM9999th') AS rank FROM + \\(SELECT albums.name AS album_name, albums.id AS album_id, COUNT(DISTINCT songs.id) AS song_num, COUNT(scrobbles) AS scrobbles + \\FROM albumsongs + \\INNER JOIN albums ON albumsongs.album_id = albums.id + \\INNER JOIN scrobbles ON scrobbles.albumsong = albumsongs.id \\INNER JOIN songs ON songs.id = albumsongs.song_id - \\GROUP BY albums.id) - \\SELECT * FROM (SELECT album_name, scrobbles, TO_CHAR(rank,'FM9999th') AS rank, album_id, song_num, - \\( rank = lag(rank, 1, -1::bigint) OVER (ORDER BY rank) - \\OR rank = lead(rank, 1, -1::bigint) OVER (ORDER BY rank) - \\)AS is_tie - \\FROM ranked) WHERE album_id = $1; + \\GROUP BY albums.id)) AS t + \\INNER JOIN artistalbums ON artistalbums.album_id = t.album_id + \\INNER JOIN artists ON artists.id = artistalbums.artist_id + \\WHERE t.album_id = $1 , .artist => - \\WITH ranked AS ( - \\SELECT artists.name AS artist_name, COUNT(artists.id) AS scrobbles, RANK() OVER ( ORDER BY COUNT(artists.id) DESC) AS rank, artists.id AS artist_id, COUNT(DISTINCT songs.id) AS song_num, COUNT(DISTINCT albums.id) AS album_num - \\FROM scrobbles - \\INNER JOIN albumsongs ON albumsongs.id = scrobbles.albumsong - \\INNER JOIN albumsongsartists ON albumsongsartists.albumsong_id = scrobbles.albumsong + \\SELECT * FROM + \\(SELECT *, TO_CHAR(ROW_NUMBER() OVER (ORDER BY scrobbles DESC),'FM9999th') AS rank FROM + \\(SELECT artists.name AS artist_name, artists.id AS artist_id, COUNT(scrobbles) AS scrobbles, COUNT(DISTINCT albums.id) AS album_num, COUNT(DISTINCT songs.id) AS song_num + \\FROM albumsongsartists + \\INNER JOIN artists ON albumsongsartists.artist_id = artists.id + \\INNER JOIN albumsongs ON albumsongsartists.albumsong_id = albumsongs.id + \\INNER JOIN scrobbles ON scrobbles.albumsong = albumsongs.id \\INNER JOIN albums ON albums.id = albumsongs.album_id \\INNER JOIN songs ON songs.id = albumsongs.song_id - \\INNER JOIN artists ON artists.id = albumsongsartists.artist_id - \\GROUP BY artists.id) - \\SELECT * FROM (SELECT artist_name, scrobbles, TO_CHAR(rank,'FM9999th') AS rank, artist_id, song_num, album_num, - \\( rank = lag(rank, 1, -1::bigint) OVER (ORDER BY rank) - \\OR rank = lead(rank, 1, -1::bigint) OVER (ORDER BY rank) - \\)AS is_tie - \\FROM ranked) WHERE artist_id = $1; + \\GROUP BY artists.id)) + \\WHERE artist_id = $1 , }, @@ -504,45 +454,21 @@ pub fn loadQuery(comptime entity: EntityType, comptime query_type: QueryTypeEnum , else => unreachable, }, - .friends => switch (entity) { - .song => - \\SELECT name, COUNT(DISTINCT dt) AS days, COUNT(dt) AS plays - \\FROM ( - \\ SELECT scrobbles.albumsong, songs.name, TO_CHAR(scrobbles.datetime, 'YYYY-MM-DD') AS dt - \\ FROM scrobbles - \\ INNER JOIN albumsongs ON albumsongs.id = scrobbles.albumsong - \\ INNER JOIN songs ON songs.id = albumsongs.song_id - \\ WHERE TO_CHAR(datetime,'YYYY-MM-DD') IN ( - \\ SELECT DISTINCT TO_CHAR(datetime,'YYYY-MM-DD') - \\ FROM scrobbles - \\ WHERE albumsong = $1 - \\ ) - \\) GROUP BY albumsong, name ORDER BY days DESC, plays DESC, name ASC; - , - else => unreachable, - }, - .peak => switch (entity) { - .scrobble => @compileError("Cannot specify scrobble for peak"), - .song => - \\SELECT songs.id AS eid, TO_CHAR(scrobbles.datetime, 'YYYY-MM-DD') AS datetime FROM scrobbles - \\INNER JOIN albumsongs ON albumsongs.id = scrobbles.albumsong - \\INNER JOIN songs ON songs.id = albumsongs.song_id - \\ORDER BY datetime ASC; - , - .album => - \\SELECT albums.id AS eid, TO_CHAR(scrobbles.datetime, 'YYYY-MM-DD') AS datetime FROM scrobbles - \\INNER JOIN albumsongs ON albumsongs.id = scrobbles.albumsong - \\INNER JOIN albums ON albums.id = albumsongs.album_id - \\ORDER BY datetime ASC; - , - .artist => - \\SELECT artists.id AS eid, TO_CHAR(scrobbles.datetime, 'YYYY-MM-DD') AS datetime FROM scrobbles - \\INNER JOIN albumsongs ON albumsongs.id = scrobbles.albumsong - \\INNER JOIN albumsongsartists ON albumsongsartists.albumsong_id = albumsongs.id - \\INNER JOIN artists ON artists.id = albumsongsartists.artist_id - \\ORDER BY datetime ASC; - , - }, }, }; } + +// I'm pretty sure this query will tell you the number of days a song was played on the same day as a specified song +// The output looked right at least. Can easily be changed into the number of times a song was played on the same day +// as a specified song by removing DISTINCT from the first subquery (which would increase the count if a song was +// played more than once in a day) + +//SELECT COUNT(albumsong), name +//FROM ( +// SELECT DISTINCT scrobbles.albumsong, songs.name, TO_CHAR(scrobbles.datetime, 'YYYY-MM-DD') +// FROM scrobbles +// INNER JOIN albumsongs ON albumsongs.id = scrobbles.albumsong +// INNER JOIN songs ON songs.id = albumsongs.song_id +// WHERE TO_CHAR(datetime,'YYYY-MM-DD') IN ( +// SELECT DISTINCT TO_CHAR(datetime,'YYYY-MM-DD') FROM scrobbles WHERE albumsong = $1 +//)) GROUP BY albumsong, name ORDER BY COUNT(albumsong) DESC, name ASC;