From 0b7efc3420e7e3d5926a04341a93ae492232aaae Mon Sep 17 00:00:00 2001 From: mitteneer Date: Tue, 24 Jun 2025 00:05:25 -0400 Subject: [PATCH] Begin album reviews Album reviews would ideally allow you to rate tracks at the same time, so we'll have to work on that next. Also, disambiguation pages are becoming more and more necessary (Little Talks in inaccessible atm) Preferably, we start working on the `INDEX` for `/ratings` as well, and maybe use a unified language for these things (is it review, rating, rating_text, score,...?) --- src/app/views/albums.zig | 37 +++++++++++++++++++++--- src/app/views/albums/get.zmpl | 40 +++++++++++++++++++++----- src/app/views/ratings/albums.zig | 14 +++++++++ src/app/views/ratings/albums/post.zmpl | 1 + src/app/views/songs/get.zmpl | 22 +++++++------- src/queries.zig | 6 ++++ 6 files changed, 98 insertions(+), 22 deletions(-) create mode 100644 src/app/views/ratings/albums.zig create mode 100644 src/app/views/ratings/albums/post.zmpl diff --git a/src/app/views/albums.zig b/src/app/views/albums.zig index 03046b3..17421e7 100644 --- a/src/app/views/albums.zig +++ b/src/app/views/albums.zig @@ -4,6 +4,7 @@ const jetquery = @import("jetzig").jetquery; const TableRow = @import("../../types.zig").TableRow; const HyperlinkData = @import("../../types.zig").HyperlinkData; const queries = @import("../../queries.zig"); +const decode = @import("../../date_fmt.zig").urlDecode; pub fn index(request: *jetzig.Request) !jetzig.View { var root = try request.data(.object); @@ -15,19 +16,47 @@ pub fn index(request: *jetzig.Request) !jetzig.View { } pub fn get(id: []const u8, request: *jetzig.Request) !jetzig.View { + const parse_err = blk: { + const rdr_id = std.fmt.parseInt(i64, id, 10) catch |err| break :blk err; + const album = try jetzig.database.Query(.Album).find(rdr_id).execute(request.repo); + if (album == null) break :blk error.InvalidCharacter; + var name = std.ArrayList(u8).init(request.allocator); + try name.appendSlice("http://127.0.0.1:8080/albums/"); + try name.appendSlice(album.?.name); + return request.redirect(try name.toOwnedSlice(), .found); + }; + + const id_int = switch (parse_err) { + error.Overflow => return request.fail(.not_found), + error.InvalidCharacter => blk: { + const rn = try decode(request.allocator, id); + std.log.debug("{s}", .{rn}); + const songs = try jetzig.database.Query(.Album).where(.{ .name = rn }).all(request.repo); + + if (songs.len == 0) return request.fail(.not_found); + if (songs.len > 1) return request.redirect("http://127.0.0.1:8080", .found); + break :blk songs[0].id; + }, + }; var root = try request.data(.object); - const album = try queries.entityQueryResult(request, queries.loadQuery(.album, .entity_info), .{id}); + const album = try queries.entityQueryResult(request, queries.loadQuery(.album, .entity_info), .{id_int}); try root.put("album", album); - const songs = try queries.entityQueryResult(request, queries.loadQuery(.album, .get_songs), .{id}); + const scrobbles = try queries.entityQueryResult(request, queries.loadQuery(.song, .get_scrobbles), .{id_int}); + try root.put("scrobbles", scrobbles); + + const songs = try queries.entityQueryResult(request, queries.loadQuery(.album, .get_songs), .{id_int}); try root.put("songs", songs); - const firstlast = try queries.entityQueryResult(request, queries.loadQuery(.album, .firstlast), .{id}); + const firstlast = try queries.entityQueryResult(request, queries.loadQuery(.album, .firstlast), .{id_int}); try root.put("firstlast", firstlast); - const timescale = try queries.entityQueryResult(request, queries.loadQuery(.album, .timescale), .{id}); + const timescale = try queries.entityQueryResult(request, queries.loadQuery(.album, .timescale), .{id_int}); try root.put("yearly", timescale); + const ratings = try queries.entityQueryResult(request, queries.loadQuery(.song, .get_ratings), .{id_int}); + try root.put("reviews", ratings); + return request.render(.ok); } diff --git a/src/app/views/albums/get.zmpl b/src/app/views/albums/get.zmpl index 7e089e7..6dfd7f7 100644 --- a/src/app/views/albums/get.zmpl +++ b/src/app/views/albums/get.zmpl @@ -1,22 +1,48 @@ @zig { const ColumnChoices = []const enum{song, album, artist, artistlist, scrobbles, date}; const columns: ColumnChoices = &.{.song, .scrobbles}; + const reviews = try zmpl.coerceArray(".reviews"); } + @partial partials/header +

{{.album.album_name}}

{{.album.artist_name}}

-
{{.album.scrobbles}} scrobbles ({{.album.rank}} place)
-
{{.album.song_num}} songs
-@partial partials/firstlast_listens(firstlast: .firstlast) -

Yearly Performance

-@partial partials/timescale(range: .yearly) -

Songs

-@partial partials/newtable(T: ColumnChoices, table_data: .songs, columns: columns) +
+ +
+
+
{{.album.scrobbles}} scrobbles ({{.album.rank}} place)
+
{{.album.song_num}} songs
+ @partial partials/firstlast_listens(firstlast: .firstlast) +

Yearly Performance

+ @partial partials/timescale(range: .yearly) +

Songs

+ @partial partials/newtable(T: ColumnChoices, table_data: .songs, columns: columns) +
+
+

Rating

+
+ @zig { + if (reviews.len == 0) { +
+ + + +
+ } else { + for (reviews) |review| { + {{review.score}}: {{review.review}} ({{review.date}}) + } + } + } +
+
\ No newline at end of file diff --git a/src/app/views/ratings/albums.zig b/src/app/views/ratings/albums.zig new file mode 100644 index 0000000..a57adfa --- /dev/null +++ b/src/app/views/ratings/albums.zig @@ -0,0 +1,14 @@ +const std = @import("std"); +const jetzig = @import("jetzig"); +pub fn post(request: *jetzig.Request) !jetzig.View { + var root = try request.data(.object); + const params = try request.params(); + const id = params.getT(.integer, "album_id").?; + const score = if (params.getT(.integer, "score")) |score| @as(i16, @truncate(score)) else null; + const review = params.getT(.string, "review"); + try jetzig.database.Query(.Albumrating).insert(.{ .album = id, .rating = score, .rating_text = review, .date = @divFloor(request.start_time, 1_000) }).execute(request.repo); + try root.put("score", score); + try root.put("review", review); + + return request.render(.created); +} diff --git a/src/app/views/ratings/albums/post.zmpl b/src/app/views/ratings/albums/post.zmpl new file mode 100644 index 0000000..ca54fd7 --- /dev/null +++ b/src/app/views/ratings/albums/post.zmpl @@ -0,0 +1 @@ + {{.score}}: {{.review}} (Today) \ No newline at end of file diff --git a/src/app/views/songs/get.zmpl b/src/app/views/songs/get.zmpl index f07b03c..c092ea5 100644 --- a/src/app/views/songs/get.zmpl +++ b/src/app/views/songs/get.zmpl @@ -28,17 +28,17 @@

Rating

@zig { - if (reviews.len == 0) { -
- - - -
- } else { - for (reviews) |review| { - {{review.score}}: {{review.review}} ({{review.date}}) - } -} + if (reviews.len == 0) { +
+ + + +
+ } else { + for (reviews) |review| { + {{review.score}}: {{review.review}} ({{review.date}}) + } + } }
diff --git a/src/queries.zig b/src/queries.zig index 0c9359e..9010942 100644 --- a/src/queries.zig +++ b/src/queries.zig @@ -446,6 +446,12 @@ pub fn loadQuery(entity: EntityType, query_type: QueryTypeEnum) GeneratedQuery { \\WHERE song = $1 \\ORDER BY date DESC; , + .album => + \\SELECT rating AS score, rating_text AS review, TO_CHAR(date, 'YYY-MM-DD') AS date + \\FROM albumratings + \\WHERE album = $1 + \\ORDER BY date DESC; + , else => unreachable, }, },