Compare commits

...

6 commits

Author SHA1 Message Date
153ea869e0 Work on making partials for views 2025-05-08 18:17:45 -04:00
4758885c68 Keep cleaning 2025-05-05 13:09:16 -04:00
9ffc45b207 Delete common_queries.md
No longer relevant
2025-05-05 11:15:30 -04:00
94cc6e3bd5 Remove unused views and functions 2025-05-05 11:06:52 -04:00
c574885f8d Get rid of unused views 2025-05-05 10:37:18 -04:00
762a4fd51e Create partial for view agnostic table 2025-05-02 10:00:47 -04:00
78 changed files with 135 additions and 1129 deletions

View file

@ -17,12 +17,12 @@
// internet connectivity.
.dependencies = .{
.jetzig = .{
.url = "https://github.com/jetzig-framework/jetzig/archive/1feb18fb74e626fe068ec67532318640a9cb83be.tar.gz",
.hash = "jetzig-0.0.0-IpAgLfMzDwDyAqZ05btcLDd9dfE_bxUbfOI_Wx7a19ed",
.url = "https://github.com/jetzig-framework/jetzig/archive/a298192bb0cddf9c45d7d0d976a9852804457de2.tar.gz",
.hash = "jetzig-0.0.0-IpAgLf5aDwB4UOKMhIjtK22zBsPfbWEwCYgHT0hayyT-",
},
.zeit = .{
.url = "https://github.com/rockorager/zeit/archive/refs/heads/main.tar.gz",
.hash = "zeit-0.6.0-5I6bk5daAgC-P60TjxRqW0bYknfCGxJp-03eS9UjGrO7",
.url = "https://github.com/rockorager/zeit/archive/refs/tags/v0.6.0.tar.gz",
.hash = "zeit-0.0.0-5I6bk_pZAgB03N1p1GmVOZ--gOFwwQSRKj1UXb5tnaKS",
},
},
.paths = .{

View file

@ -1,164 +0,0 @@
Get all albums from specified artist:
```sql
SELECT artists.name, albums.name
FROM "Albumartists"
INNER JOIN artists
ON "Albumartists".artist_id = artists.id
INNER JOIN albums
ON "Albumartists".album_id = albums.id
WHERE artists.name = {ARTIST};
```
Get all songs from specified artist:
```sql
SELECT artists.name, songs.name
FROM "Songartists"
INNER JOIN artists
ON "Songartists".artist_id = artists.id
INNER JOIN songs
ON "Songartists".song_id = songs.id
WHERE artists.name = {ARTIST};
```
Get all songs from any album of the specified name:
```sql
SELECT songs.name
FROM "Albumsongs"
INNER JOIN albums
ON "Albumsongs".album_id = albums.id
INNER JOIN songs
ON "Albumsongs".song_id = songs.id
WHERE albums.name = {ALBUM};
```
Sort all songs by plays (does not list artist or album):
```sql
SELECT songs.name, COUNT(scrobbles.song_id) AS scount
FROM songs, scrobbles
WHERE songs.id = scrobbles.song_id
GROUP BY songs.id
ORDER BY scount DESC;
```
Sort all songs by plays, and include artist:
```sql
SELECT songs.name, artists.name, COUNT(scrobbles.song_id) AS scount
FROM scrobbles, "Songartists"
INNER JOIN artists
ON "Songartists".artist_id = artists.id
INNER JOIN songs
ON "Songartists".song_id = songs.id
WHERE songs.id = scrobbles.song_id
GROUP BY songs.id, artists.id
ORDER BY scount DESC;
```
Sort all songs by plays, and include artist and album:
```sql
SELECT songs.name, artists.name, albums.name, COUNT(scrobbles.song_id) AS scount
FROM scrobbles CROSS JOIN "Songartists" CROSS JOIN "Albumsongs"
INNER JOIN artists
ON "Songartists".artist_id = artists.id
INNER JOIN songs
ON "Songartists".song_id = songs.id AND "Albumsongs".song_id = songs.id
INNER JOIN albums
ON "Albumsongs".album_id = albums.id
WHERE songs.id = scrobbles.song_id
GROUP BY songs.id, artists.id, albums.id
ORDER BY scount DESC;
```
Sort all albums by plays, and include artist:
```sql
SELECT albums.name, artists.name, COUNT(scrobbles.album_id) AS scount
FROM scrobbles, "Albumartists"
INNER JOIN albums
ON "Albumartists".album_id = albums.id
INNER JOIN artists
ON "Albumartists".artist_id = artists.id
WHERE albums.id = scrobbles.album_id
GROUP BY artists.id, albums.id
ORDER BY scount DESC;
```
Sort all artists by plays:
```sql
SELECT artists.name, COUNT(scrobbles.id) AS scount
FROM artists, "Scrobbleartists"
INNER JOIN scrobbles
ON scrobbles.id = "Scrobbleartists".scrobble_id
WHERE "Scrobbleartists".artist_id = artists.id
GROUP BY artists.id
ORDER BY scount DESC;
```
Sort all artists by alphabetical order, and include the first time you listened to that artist:
```sql
SELECT artists.name, MIN(scrobbles.date)
FROM "Scrobbleartists"
INNER JOIN artists
ON "Scrobbleartists".artist_id = artists.id
INNER JOIN scrobbles
ON "Scrobbleartists".scrobble_id = scrobbles.id
GROUP BY artists.id
ORDER BY artists.name ASC;
```
Sort all songs by alphabetical order, and include the first time you listened to that song:
```sql
SELECT songs.name, MIN(scrobbles.date)
FROM scrobbles
INNER JOIN songs
ON scrobbles.song_id = songs.id
GROUP BY songs.id
ORDER BY songs.name ASC;
```
Sort all albums by alphabetical order, and include the first time you listened to that album:
```sql
SELECT albums.name, MIN(scrobbles.date)
FROM scrobbles
INNER JOIN albums
ON scrobbles.album_id = albums.id
GROUP BY albums.id
ORDER BY albums.name ASC;
```
Select all songs by specified artists, include the number of plays of each song, and sort by plays:
```sql
SELECT songs.name, COUNT(scrobbles.song_id) as count
FROM songs, "Scrobbleartists"
INNER JOIN artists
ON "Scrobbleartists".artist_id = artists.id
INNER JOIN scrobbles
ON "Scrobbleartists".scrobble_id = scrobbles.id
WHERE songs.id = scrobbles.song_id AND artists.name = {ARTIST}
GROUP BY songs.id
ORDER BY count DESC;
```
Select all albums by specified artist, include the number of plays of each album, and sort by plays:
```sql
SELECT albums.name, COUNT(scrobbles.song_id) as count
FROM albums, "Scrobbleartists"
INNER JOIN artists
ON "Scrobbleartists".artist_id = artists.id
INNER JOIN scrobbles
ON "Scrobbleartists".scrobble_id = scrobbles.id
WHERE albums.id = scrobbles.album_id AND artists.name = {ARTIST}
GROUP BY albums.id
ORDER BY count DESC;
```
Select all songs from an album specified by an ID, and sort by plays
```sql
SELECT songs.name, COUNT(scrobbles.song_id) AS count
FROM "Albumsongs"
INNER JOIN songs
ON songs.id = "Albumsongs".song_id
INNER JOIN scrobbles
ON scrobbles.song_id = "Albumsongs".song_id
WHERE "Albumsongs".album_id = {ALBUM_ID}
GROUP BY songs.id
ORDER BY count DESC;
```

View file

@ -1,65 +0,0 @@
/// Demo middleware. Assign middleware by declaring `pub const middleware` in the
/// `jetzig_options` defined in your application's `src/main.zig`.
///
/// Middleware is called before and after the request, providing full access to the active
/// request, allowing you to execute any custom code for logging, tracking, inserting response
/// headers, etc.
///
/// This middleware is configured in the demo app's `src/main.zig`:
///
/// ```
/// pub const jetzig_options = struct {
/// pub const middleware: []const type = &.{@import("app/middleware/DemoMiddleware.zig")};
/// };
/// ```
const std = @import("std");
const jetzig = @import("jetzig");
/// Define any custom data fields you want to store here. Assigning to these fields in the `init`
/// function allows you to access them in various middleware callbacks defined below, where they
/// can also be modified.
my_custom_value: []const u8,
const Self = @This();
/// Initialize middleware.
pub fn init(request: *jetzig.http.Request) !*Self {
var middleware = try request.allocator.create(Self);
middleware.my_custom_value = "initial value";
return middleware;
}
/// Invoked immediately after the request is received but before it has started processing.
/// Any calls to `request.render` or `request.redirect` will prevent further processing of the
/// request, including any other middleware in the chain.
pub fn afterRequest(self: *Self, request: *jetzig.http.Request) !void {
try request.server.logger.DEBUG(
"[DemoMiddleware:afterRequest] my_custom_value: {s}",
.{self.my_custom_value},
);
self.my_custom_value = @tagName(request.method);
}
/// Invoked immediately before the response renders to the client.
/// The response can be modified here if needed.
pub fn beforeResponse(self: *Self, request: *jetzig.http.Request, response: *jetzig.http.Response) !void {
try request.server.logger.DEBUG(
"[DemoMiddleware:beforeResponse] my_custom_value: {s}, response status: {s}",
.{ self.my_custom_value, @tagName(response.status_code) },
);
}
/// Invoked immediately after the response has been finalized and sent to the client.
/// Response data can be accessed for logging, but any modifications will have no impact.
pub fn afterResponse(self: *Self, request: *jetzig.http.Request, response: *jetzig.http.Response) !void {
_ = self;
_ = response;
try request.server.logger.DEBUG("[DemoMiddleware:afterResponse] response completed", .{});
}
/// Invoked after `afterResponse` is called. Use this function to do any clean-up.
/// Note that `request.allocator` is an arena allocator, so any allocations are automatically
/// freed before the next request starts processing.
pub fn deinit(self: *Self, request: *jetzig.http.Request) void {
request.allocator.destroy(self);
}

View file

@ -29,19 +29,19 @@ pub fn index(request: *jetzig.Request) !jetzig.View {
if (album.id == prev_album_id) {
var artist_info = try prev_artist_infos.?.append(.object);
try artist_info.put("name", album.artist_name);
try artist_info.put("url", album.artist_id);
try artist_info.put("id", album.artist_id);
continue :blk;
}
var album_view = try albums_view.append(.object);
try album_view.put("name", album.name);
try album_view.put("id", album.id);
try album_view.put("scrobbles", album.scrobbles);
var artist_infos = try album_view.put("artist_info", .array);
var artist_info = try artist_infos.append(.object);
try artist_info.put("name", album.artist_name);
try artist_info.put("url", album.artist_id);
try album_view.put("name", album.name);
try album_view.put("url", album.id);
try album_view.put("scrobbles", album.scrobbles);
try artist_info.put("id", album.artist_id);
prev_artist_infos = artist_infos;
prev_album_id = album.id;
@ -74,100 +74,8 @@ pub fn get(id: []const u8, request: *jetzig.Request) !jetzig.View {
const song = try song_row.to(Song, .{ .dupe = true, .allocator = request.allocator });
var song_view = try songs_view.append(.object);
try song_view.put("name", song.name);
try song_view.put("url", song.id);
try song_view.put("id", song.id);
try song_view.put("scrobbles", song.scrobbles);
}
return request.render(.ok);
}
pub fn new(request: *jetzig.Request) !jetzig.View {
return request.render(.ok);
}
pub fn edit(id: []const u8, request: *jetzig.Request) !jetzig.View {
_ = id;
return request.render(.ok);
}
pub fn post(request: *jetzig.Request) !jetzig.View {
return request.render(.created);
}
pub fn put(id: []const u8, request: *jetzig.Request) !jetzig.View {
_ = id;
return request.render(.ok);
}
pub fn patch(id: []const u8, request: *jetzig.Request) !jetzig.View {
_ = id;
return request.render(.ok);
}
pub fn delete(id: []const u8, request: *jetzig.Request) !jetzig.View {
_ = id;
return request.render(.ok);
}
test "index" {
var app = try jetzig.testing.app(std.testing.allocator, @import("routes"));
defer app.deinit();
const response = try app.request(.GET, "/album", .{});
try response.expectStatus(.ok);
}
test "get" {
var app = try jetzig.testing.app(std.testing.allocator, @import("routes"));
defer app.deinit();
const response = try app.request(.GET, "/album/example-id", .{});
try response.expectStatus(.ok);
}
test "new" {
var app = try jetzig.testing.app(std.testing.allocator, @import("routes"));
defer app.deinit();
const response = try app.request(.GET, "/album/new", .{});
try response.expectStatus(.ok);
}
test "edit" {
var app = try jetzig.testing.app(std.testing.allocator, @import("routes"));
defer app.deinit();
const response = try app.request(.GET, "/album/example-id/edit", .{});
try response.expectStatus(.ok);
}
test "post" {
var app = try jetzig.testing.app(std.testing.allocator, @import("routes"));
defer app.deinit();
const response = try app.request(.POST, "/album", .{});
try response.expectStatus(.created);
}
test "put" {
var app = try jetzig.testing.app(std.testing.allocator, @import("routes"));
defer app.deinit();
const response = try app.request(.PUT, "/album/example-id", .{});
try response.expectStatus(.ok);
}
test "patch" {
var app = try jetzig.testing.app(std.testing.allocator, @import("routes"));
defer app.deinit();
const response = try app.request(.PATCH, "/album/example-id", .{});
try response.expectStatus(.ok);
}
test "delete" {
var app = try jetzig.testing.app(std.testing.allocator, @import("routes"));
defer app.deinit();
const response = try app.request(.DELETE, "/album/example-id", .{});
try response.expectStatus(.ok);
}

View file

@ -1,3 +0,0 @@
<div>
<span>Content goes here</span>
</div>

View file

@ -1,3 +0,0 @@
<div>
<span>Content goes here</span>
</div>

View file

@ -1,3 +1,8 @@
@zig {
const ColumnChoices = []const enum{song, album, artist, artistlist, scrobbles, date};
const columns: ColumnChoices = &.{.song, .scrobbles};
}
<html>
<head>
<meta charset="UTF-8">
@ -5,15 +10,6 @@
<body>
@partial partials/header
<h1>{{.album}}</h1>
<table>
<tr>
<th>Name</th>
@for (.songs) |song| {
<tr>
<td class=cell><a href="/songs/{{song.url}}">{{song.name}}</a></td>
<td class=cell>{{song.scrobbles}}</td>
</tr>
}
</table>
@partial partials/newtable(T: ColumnChoices, table_data: .songs, columns: columns)
</body>
</html>

View file

@ -1,40 +1,15 @@
@zig {
const ColumnChoices = []const enum{song, album, artist, artistlist, scrobbles, date};
const columns: ColumnChoices = &.{.album, .artistlist, .scrobbles};
}
<html>
<head>
<link href="https://cdn.jsdelivr.net/npm/simple-datatables@latest/dist/style.css" rel="stylesheet" type="text/css">
<meta charset="UTF-8">
</head>
<body>
@partial partials/header
<h1>Albums</h1>
<table id="myTable">
<thead>
<tr>
<th>Name</th>
<th>Artist(s)</th>
<th>Scrobbles</th>
</tr>
</thead>
</tbody>
@for (.albums) |album| {
<tr>
<td class=cell><a href="/albums/{{album.url}}">{{album.name}}</a></td>
<td class=cell>
@for (album.get("artist_info").?) |ai| {
<a href="/artists/{{ai.url}}">{{ai.name}}</a>
}
</td>
<td class=cell>{{album.scrobbles}}</td>
</tr>
}
</tbody>
</table>
<script src="https://cdn.jsdelivr.net/npm/simple-datatables@latest" type="text/javascript"></script>
<script>
const dataTable = new simpleDatatables.DataTable("#myTable", {
searchable: false,
perPage: 50,
perPageSelect: [25,50,100],
});
</script>
@partial partials/newtable(T: ColumnChoices, table_data: .albums, columns: columns)
</body>
</html>

View file

@ -1,3 +0,0 @@
<div>
<span>Content goes here</span>
</div>

View file

@ -1,3 +0,0 @@
<div>
<span>Content goes here</span>
</div>

View file

@ -1,3 +0,0 @@
<div>
<span>Content goes here</span>
</div>

View file

@ -1,3 +0,0 @@
<div>
<span>Content goes here</span>
</div>

View file

@ -138,113 +138,25 @@ pub fn get(id: []const u8, request: *jetzig.Request) !jetzig.View {
defer first_last_songs_jq_result.deinit();
// These look backwards, but it's correct this way
var last_song_view = try root.put("last", .object);
if (try first_last_songs_jq_result.postgresql.result.next()) |last_song_row| {
const last_song = try last_song_row.to(ScrobbleResult, .{ .dupe = true, .allocator = request.allocator });
try root.put("last_song_name", last_song.name);
try root.put("last_song_id", last_song.id);
try root.put("last_song_date", (try dateFmt(request.allocator, last_song.date)));
}
try last_song_view.put("name", last_song.name);
try last_song_view.put("id", last_song.id);
try last_song_view.put("date", (try dateFmt(request.allocator, last_song.date)));
} else unreachable;
var first_song_view = try root.put("first", .object);
if (try first_last_songs_jq_result.postgresql.result.next()) |first_song_row| {
const first_song = try first_song_row.to(ScrobbleResult, .{ .dupe = true, .allocator = request.allocator });
try root.put("first_song_name", first_song.name);
try root.put("first_song_id", first_song.id);
try root.put("first_song_date", (try dateFmt(request.allocator, first_song.date)));
}
try first_song_view.put("name", first_song.name);
try first_song_view.put("id", first_song.id);
try first_song_view.put("date", (try dateFmt(request.allocator, first_song.date)));
} else unreachable;
try first_last_songs_jq_result.drain();
return request.render(.ok);
}
pub fn new(request: *jetzig.Request) !jetzig.View {
return request.render(.ok);
}
pub fn edit(id: []const u8, request: *jetzig.Request) !jetzig.View {
_ = id;
return request.render(.ok);
}
pub fn post(request: *jetzig.Request) !jetzig.View {
return request.render(.created);
}
pub fn put(id: []const u8, request: *jetzig.Request) !jetzig.View {
_ = id;
return request.render(.ok);
}
pub fn patch(id: []const u8, request: *jetzig.Request) !jetzig.View {
_ = id;
return request.render(.ok);
}
pub fn delete(id: []const u8, request: *jetzig.Request) !jetzig.View {
_ = id;
return request.render(.ok);
}
test "index" {
var app = try jetzig.testing.app(std.testing.allocator, @import("routes"));
defer app.deinit();
const response = try app.request(.GET, "/artist", .{});
try response.expectStatus(.ok);
}
test "get" {
var app = try jetzig.testing.app(std.testing.allocator, @import("routes"));
defer app.deinit();
const response = try app.request(.GET, "/artist/example-id", .{});
try response.expectStatus(.ok);
}
test "new" {
var app = try jetzig.testing.app(std.testing.allocator, @import("routes"));
defer app.deinit();
const response = try app.request(.GET, "/artist/new", .{});
try response.expectStatus(.ok);
}
test "edit" {
var app = try jetzig.testing.app(std.testing.allocator, @import("routes"));
defer app.deinit();
const response = try app.request(.GET, "/artist/example-id/edit", .{});
try response.expectStatus(.ok);
}
test "post" {
var app = try jetzig.testing.app(std.testing.allocator, @import("routes"));
defer app.deinit();
const response = try app.request(.POST, "/artist", .{});
try response.expectStatus(.created);
}
test "put" {
var app = try jetzig.testing.app(std.testing.allocator, @import("routes"));
defer app.deinit();
const response = try app.request(.PUT, "/artist/example-id", .{});
try response.expectStatus(.ok);
}
test "patch" {
var app = try jetzig.testing.app(std.testing.allocator, @import("routes"));
defer app.deinit();
const response = try app.request(.PATCH, "/artist/example-id", .{});
try response.expectStatus(.ok);
}
test "delete" {
var app = try jetzig.testing.app(std.testing.allocator, @import("routes"));
defer app.deinit();
const response = try app.request(.DELETE, "/artist/example-id", .{});
try response.expectStatus(.ok);
}

View file

@ -1,3 +0,0 @@
<div>
<span>Content goes here</span>
</div>

View file

@ -1,3 +0,0 @@
<div>
<span>Content goes here</span>
</div>

View file

@ -1,53 +1,22 @@
@zig {
const ColumnChoices = []const enum{song, album, artist, artistlist, scrobbles, date};
const columns: ColumnChoices = &.{.album, .scrobbles};
}
<html>
<head>
<link href="https://cdn.jsdelivr.net/npm/simple-datatables@latest/dist/style.css" rel="stylesheet" type="text/css">
<meta charset="UTF-8">
</head>
<body>
@partial partials/header
<h1>{{.artist.name}}</h1>
{{.artist.scrobbles}} scrobbles ({{.artist.rank}} place)
<br>
First listen: <a href="/songs/{{.first_song_id}}">{{.first_song_name}}</a> ({{.first_song_date}})
<br>
Most recent listen: <a href="/songs/{{.last_song_id}}">{{.last_song_name}}</a> ({{.last_song_date}})
@partial partials/firstlast_listens(scrobbles: .artist.scrobbles, rank: .artist.rank, last_song: .last, first_song: .first)
<h2>Albums</h2>
<table id="myTable">
<thead>
<tr>
<th>Name</th><th>Scrobbles</th>
</tr>
</thead>
<tbody>
@for (.albums) |album| {
<tr>
<td class=cell><a href="/albums/{{album.id}}">{{album.name}}</a></td>
<td class=cell>{{album.scrobbles}}</td>
</tr>
}
</tbody>
</table>
@partial partials/newtable(T: ColumnChoices, table_data: .albums, columns: columns)
<h2>Albums Featured On</h2>
<table id="myTable">
<thead>
<tr>
<th>Name</th><th>Scrobbles</th>
</tr>
</thead>
<tbody>
@for (.appears) |album| {
<tr>
<td class=cell><a href="/albums/{{album.id}}">{{album.name}}</a></td>
<td class=cell>{{album.scrobbles}}</td>
</tr>
}
</tbody>
<table>
<script src="https://cdn.jsdelivr.net/npm/simple-datatables@latest" type="text/javascript"></script>
<script>
const dataTable = new simpleDatatables.DataTable("#myTable", {
searchable: false,
});
</script>
@partial partials/newtable(T: ColumnChoices, table_data: .appears, columns: columns)
</body>
</html>

View file

@ -1,3 +0,0 @@
<div>
<span>Content goes here</span>
</div>

View file

@ -1,3 +0,0 @@
<div>
<span>Content goes here</span>
</div>

View file

@ -1,3 +0,0 @@
<div>
<span>Content goes here</span>
</div>

View file

@ -1,3 +0,0 @@
<div>
<span>Content goes here</span>
</div>

View file

@ -11,26 +11,3 @@ pub fn get(id: []const u8, request: *jetzig.Request, data: *jetzig.Data) !jetzig
_ = id;
return request.render(.ok);
}
pub fn post(request: *jetzig.Request, data: *jetzig.Data) !jetzig.View {
_ = data;
return request.render(.created);
}
pub fn put(id: []const u8, request: *jetzig.Request, data: *jetzig.Data) !jetzig.View {
_ = data;
_ = id;
return request.render(.ok);
}
pub fn patch(id: []const u8, request: *jetzig.Request, data: *jetzig.Data) !jetzig.View {
_ = data;
_ = id;
return request.render(.ok);
}
pub fn delete(id: []const u8, request: *jetzig.Request, data: *jetzig.Data) !jetzig.View {
_ = data;
_ = id;
return request.render(.ok);
}

View file

@ -1,3 +0,0 @@
<div>
<span>Content goes here</span>
</div>

View file

@ -1,3 +0,0 @@
<div>
<span>Content goes here</span>
</div>

View file

@ -1,3 +0,0 @@
<div>
<span>Content goes here</span>
</div>

View file

@ -1,3 +0,0 @@
<div>
<span>Content goes here</span>
</div>

View file

@ -11,26 +11,3 @@ pub fn get(id: []const u8, request: *jetzig.Request, data: *jetzig.Data) !jetzig
_ = id;
return request.render(.ok);
}
pub fn post(request: *jetzig.Request, data: *jetzig.Data) !jetzig.View {
_ = data;
return request.render(.created);
}
pub fn put(id: []const u8, request: *jetzig.Request, data: *jetzig.Data) !jetzig.View {
_ = data;
_ = id;
return request.render(.ok);
}
pub fn patch(id: []const u8, request: *jetzig.Request, data: *jetzig.Data) !jetzig.View {
_ = data;
_ = id;
return request.render(.ok);
}
pub fn delete(id: []const u8, request: *jetzig.Request, data: *jetzig.Data) !jetzig.View {
_ = data;
_ = id;
return request.render(.ok);
}

View file

@ -1,3 +0,0 @@
<div>
<span>Content goes here</span>
</div>

View file

@ -1,3 +0,0 @@
<div>
<span>Content goes here</span>
</div>

View file

@ -1,3 +0,0 @@
<div>
<span>Content goes here</span>
</div>

View file

@ -1,3 +0,0 @@
<div>
<span>Content goes here</span>
</div>

View file

@ -16,21 +16,3 @@ pub fn post(request: *jetzig.Request, data: *jetzig.Data) !jetzig.View {
_ = data;
return request.render(.created);
}
pub fn put(id: []const u8, request: *jetzig.Request, data: *jetzig.Data) !jetzig.View {
_ = data;
_ = id;
return request.render(.ok);
}
pub fn patch(id: []const u8, request: *jetzig.Request, data: *jetzig.Data) !jetzig.View {
_ = data;
_ = id;
return request.render(.ok);
}
pub fn delete(id: []const u8, request: *jetzig.Request, data: *jetzig.Data) !jetzig.View {
_ = data;
_ = id;
return request.render(.ok);
}

View file

@ -1,3 +0,0 @@
<div>
<span>Content goes here</span>
</div>

View file

@ -1,3 +0,0 @@
<div>
<span>Content goes here</span>
</div>

View file

@ -1,3 +0,0 @@
<div>
<span>Content goes here</span>
</div>

View file

@ -0,0 +1,9 @@
@args scrobbles: i64, rank: []const u8, last_song: *ZmplValue, first_song: *ZmplValue
<div>
{{scrobbles}} scrobbles ({{rank}} place)
<br>
First listen: <a href="/songs/{{first_song.id}}">{{first_song.name}}</a> ({{first_song.date}})
<br>
Most recent listen: <a href="/songs/{{last_song.id}}">{{last_song.name}}</a> ({{last_song.date}})
</div>

View file

@ -0,0 +1,69 @@
@args T: type, table_data: *ZmplValue, columns: T
<table>
<thead>
<tr>
@zig {
for (columns) |header| {
switch (header) {
.song => {
<th>Song</th>
},
.album => {
<th>Album</th>
},
.artist => {
<th>Artist</th>
},
.artistlist => {
<th>Artist(s)</th>
},
.scrobbles => {
<th>Scrobbles</th>
},
.date => {
<th>Date</th>
}
}
}
}
</tr>
</thead>
<tbody>
@zig {
const array = table_data.items(.array);
for (array) |ent| {
<tr>
for (columns) |header| {
switch (header) {
.song, .album, .artist => {
const path = switch (header) {
.song => "songs",
.album => "albums",
.artist => "artists",
else => unreachable
};
<td class=cell>
<a href="/{{path}}/{{ent.id}}">{{ent.name}}</a>
</td>
},
.artistlist => {
<td class=cell>
@for (ent.get("artist_info").?) |artist| {
<a href="/artists/{{artist.id}}">{{artist.name}}</a>
}
</td>
},
.scrobbles => {
<td class=cell>{{ent.scrobbles}}</td>
},
.date =>{
<td class=cell>{{ent.date}}</td>
}
}
}
</tr>
}
}
</tbody>
</table>

View file

@ -16,21 +16,3 @@ pub fn post(request: *jetzig.Request, data: *jetzig.Data) !jetzig.View {
_ = data;
return request.render(.created);
}
pub fn put(id: []const u8, request: *jetzig.Request, data: *jetzig.Data) !jetzig.View {
_ = data;
_ = id;
return request.render(.ok);
}
pub fn patch(id: []const u8, request: *jetzig.Request, data: *jetzig.Data) !jetzig.View {
_ = data;
_ = id;
return request.render(.ok);
}
pub fn delete(id: []const u8, request: *jetzig.Request, data: *jetzig.Data) !jetzig.View {
_ = data;
_ = id;
return request.render(.ok);
}

View file

@ -1,3 +0,0 @@
<div>
<span>Content goes here</span>
</div>

View file

@ -1,3 +0,0 @@
<div>
<span>Content goes here</span>
</div>

View file

@ -1,3 +0,0 @@
<div>
<span>Content goes here</span>
</div>

View file

@ -4,21 +4,6 @@ const jetzig = @import("jetzig");
pub fn index(request: *jetzig.Request) !jetzig.View {
return request.render(.ok);
}
pub fn get(id: []const u8, request: *jetzig.Request) !jetzig.View {
_ = id;
return request.render(.ok);
}
pub fn new(request: *jetzig.Request) !jetzig.View {
return request.render(.ok);
}
pub fn edit(id: []const u8, request: *jetzig.Request) !jetzig.View {
_ = id;
return request.render(.ok);
}
pub fn post(request: *jetzig.Request) !jetzig.View {
const params = try request.params();
@ -54,82 +39,3 @@ pub fn post(request: *jetzig.Request) !jetzig.View {
return request.render(.created);
}
pub fn put(id: []const u8, request: *jetzig.Request) !jetzig.View {
_ = id;
return request.render(.ok);
}
pub fn patch(id: []const u8, request: *jetzig.Request) !jetzig.View {
_ = id;
return request.render(.ok);
}
pub fn delete(id: []const u8, request: *jetzig.Request) !jetzig.View {
_ = id;
return request.render(.ok);
}
test "index" {
var app = try jetzig.testing.app(std.testing.allocator, @import("routes"));
defer app.deinit();
const response = try app.request(.GET, "/rules", .{});
try response.expectStatus(.ok);
}
test "get" {
var app = try jetzig.testing.app(std.testing.allocator, @import("routes"));
defer app.deinit();
const response = try app.request(.GET, "/rules/example-id", .{});
try response.expectStatus(.ok);
}
test "new" {
var app = try jetzig.testing.app(std.testing.allocator, @import("routes"));
defer app.deinit();
const response = try app.request(.GET, "/rules/new", .{});
try response.expectStatus(.ok);
}
test "edit" {
var app = try jetzig.testing.app(std.testing.allocator, @import("routes"));
defer app.deinit();
const response = try app.request(.GET, "/rules/example-id/edit", .{});
try response.expectStatus(.ok);
}
test "post" {
var app = try jetzig.testing.app(std.testing.allocator, @import("routes"));
defer app.deinit();
const response = try app.request(.POST, "/rules", .{});
try response.expectStatus(.created);
}
test "put" {
var app = try jetzig.testing.app(std.testing.allocator, @import("routes"));
defer app.deinit();
const response = try app.request(.PUT, "/rules/example-id", .{});
try response.expectStatus(.ok);
}
test "patch" {
var app = try jetzig.testing.app(std.testing.allocator, @import("routes"));
defer app.deinit();
const response = try app.request(.PATCH, "/rules/example-id", .{});
try response.expectStatus(.ok);
}
test "delete" {
var app = try jetzig.testing.app(std.testing.allocator, @import("routes"));
defer app.deinit();
const response = try app.request(.DELETE, "/rules/example-id", .{});
try response.expectStatus(.ok);
}

View file

@ -1,3 +0,0 @@
<div>
<span>Content goes here</span>
</div>

View file

@ -1,3 +0,0 @@
<div>
<span>Content goes here</span>
</div>

View file

@ -1,3 +0,0 @@
<div>
<span>Content goes here</span>
</div>

View file

@ -1,3 +0,0 @@
<div>
<span>Content goes here</span>
</div>

View file

@ -1,3 +0,0 @@
<div>
<span>Content goes here</span>
</div>

View file

@ -1,3 +0,0 @@
<div>
<span>Content goes here</span>
</div>

View file

@ -55,32 +55,3 @@ pub fn index(request: *jetzig.Request) !jetzig.View {
return request.render(.ok);
}
pub fn get(id: []const u8, request: *jetzig.Request, data: *jetzig.Data) !jetzig.View {
_ = id;
_ = data;
return request.render(.ok);
}
pub fn post(request: *jetzig.Request, data: *jetzig.Data) !jetzig.View {
_ = data;
return request.render(.created);
}
pub fn put(id: []const u8, request: *jetzig.Request, data: *jetzig.Data) !jetzig.View {
_ = data;
_ = id;
return request.render(.ok);
}
pub fn patch(id: []const u8, request: *jetzig.Request, data: *jetzig.Data) !jetzig.View {
_ = data;
_ = id;
return request.render(.ok);
}
pub fn delete(id: []const u8, request: *jetzig.Request, data: *jetzig.Data) !jetzig.View {
_ = data;
_ = id;
return request.render(.ok);
}

View file

@ -1,3 +0,0 @@
<div>
<span>Content goes here</span>
</div>

View file

@ -1,3 +0,0 @@
<div>
<span>Content goes here</span>
</div>

View file

@ -1,3 +0,0 @@
<div>
<span>Content goes here</span>
</div>

View file

@ -1,3 +0,0 @@
<div>
<span>Content goes here</span>
</div>

View file

@ -1,3 +0,0 @@
<div>
<span>Content goes here</span>
</div>

View file

@ -1,104 +0,0 @@
const std = @import("std");
const jetzig = @import("jetzig");
pub fn index(request: *jetzig.Request) !jetzig.View {
return request.render(.ok);
}
pub fn get(id: []const u8, request: *jetzig.Request) !jetzig.View {
_ = id;
return request.render(.ok);
}
pub fn new(request: *jetzig.Request) !jetzig.View {
return request.render(.ok);
}
pub fn edit(id: []const u8, request: *jetzig.Request) !jetzig.View {
_ = id;
return request.render(.ok);
}
pub fn post(request: *jetzig.Request) !jetzig.View {
return request.render(.created);
}
pub fn put(id: []const u8, request: *jetzig.Request) !jetzig.View {
_ = id;
return request.render(.ok);
}
pub fn patch(id: []const u8, request: *jetzig.Request) !jetzig.View {
_ = id;
return request.render(.ok);
}
pub fn delete(id: []const u8, request: *jetzig.Request) !jetzig.View {
_ = id;
return request.render(.ok);
}
test "index" {
var app = try jetzig.testing.app(std.testing.allocator, @import("routes"));
defer app.deinit();
const response = try app.request(.GET, "/search", .{});
try response.expectStatus(.ok);
}
test "get" {
var app = try jetzig.testing.app(std.testing.allocator, @import("routes"));
defer app.deinit();
const response = try app.request(.GET, "/search/example-id", .{});
try response.expectStatus(.ok);
}
test "new" {
var app = try jetzig.testing.app(std.testing.allocator, @import("routes"));
defer app.deinit();
const response = try app.request(.GET, "/search/new", .{});
try response.expectStatus(.ok);
}
test "edit" {
var app = try jetzig.testing.app(std.testing.allocator, @import("routes"));
defer app.deinit();
const response = try app.request(.GET, "/search/example-id/edit", .{});
try response.expectStatus(.ok);
}
test "post" {
var app = try jetzig.testing.app(std.testing.allocator, @import("routes"));
defer app.deinit();
const response = try app.request(.POST, "/search", .{});
try response.expectStatus(.created);
}
test "put" {
var app = try jetzig.testing.app(std.testing.allocator, @import("routes"));
defer app.deinit();
const response = try app.request(.PUT, "/search/example-id", .{});
try response.expectStatus(.ok);
}
test "patch" {
var app = try jetzig.testing.app(std.testing.allocator, @import("routes"));
defer app.deinit();
const response = try app.request(.PATCH, "/search/example-id", .{});
try response.expectStatus(.ok);
}
test "delete" {
var app = try jetzig.testing.app(std.testing.allocator, @import("routes"));
defer app.deinit();
const response = try app.request(.DELETE, "/search/example-id", .{});
try response.expectStatus(.ok);
}

View file

@ -1,3 +0,0 @@
<div>
<span>Content goes here</span>
</div>

View file

@ -1,3 +0,0 @@
<div>
<span>Content goes here</span>
</div>

View file

@ -1,3 +0,0 @@
<div>
<span>Content goes here</span>
</div>

View file

@ -1,3 +0,0 @@
<div>
<span>Content goes here</span>
</div>

View file

@ -1,3 +0,0 @@
<div>
<span>Content goes here</span>
</div>

View file

@ -1,3 +0,0 @@
<div>
<span>Content goes here</span>
</div>

View file

@ -1,3 +0,0 @@
<div>
<span>Content goes here</span>
</div>

View file

@ -1,3 +0,0 @@
<div>
<span>Content goes here</span>
</div>

View file

@ -4,12 +4,6 @@ const jetzig = @import("jetzig");
pub fn index(request: *jetzig.Request) !jetzig.View {
var root = try request.data(.object);
var songs_view = try root.put("songs", .array);
//const songs = try jetzig.database.Query(.Song)
// .select(.{ .id, .name })
// .include(.songartists, .{ .select = .{.artist_id} })
// .include(.scrobbles, .{ .select = .{.id} })
// .orderBy(.{ .name = .asc })
// .all(request.repo);
const query =
\\SELECT songs.name, songs.id, artists.name, artists.id, COUNT(scrobbles) AS scrobbles
@ -52,24 +46,6 @@ pub fn index(request: *jetzig.Request) !jetzig.View {
prev_artist_infos = artist_infos;
prev_song_id = song.id;
}
//for (songs) |song| {
// var song_view = try songs_view.append(.object);
// var artist_infos = try song_view.put("artist_info", .array);
// for (song.songartists) |artist| {
// var artist_info = try artist_infos.append(.object);
// const artist_data = try jetzig.database.Query(.Artist)
// .find(artist.artist_id)
// .select(.{ .id, .name })
// .execute(request.repo);
// try artist_info.put("name", artist_data.?.name);
// try artist_info.put("id", artist_data.?.id);
// }
// try song_view.put("name", song.name);
// try song_view.put("url", song.id);
// try song_view.put("scrobbles", (song.scrobbles).len);
//}
return request.render(.ok);
}
@ -77,95 +53,3 @@ pub fn get(id: []const u8, request: *jetzig.Request) !jetzig.View {
_ = id;
return request.render(.ok);
}
pub fn new(request: *jetzig.Request) !jetzig.View {
return request.render(.ok);
}
pub fn edit(id: []const u8, request: *jetzig.Request) !jetzig.View {
_ = id;
return request.render(.ok);
}
pub fn post(request: *jetzig.Request) !jetzig.View {
return request.render(.created);
}
pub fn put(id: []const u8, request: *jetzig.Request) !jetzig.View {
_ = id;
return request.render(.ok);
}
pub fn patch(id: []const u8, request: *jetzig.Request) !jetzig.View {
_ = id;
return request.render(.ok);
}
pub fn delete(id: []const u8, request: *jetzig.Request) !jetzig.View {
_ = id;
return request.render(.ok);
}
test "index" {
var app = try jetzig.testing.app(std.testing.allocator, @import("routes"));
defer app.deinit();
const response = try app.request(.GET, "/song", .{});
try response.expectStatus(.ok);
}
test "get" {
var app = try jetzig.testing.app(std.testing.allocator, @import("routes"));
defer app.deinit();
const response = try app.request(.GET, "/song/example-id", .{});
try response.expectStatus(.ok);
}
test "new" {
var app = try jetzig.testing.app(std.testing.allocator, @import("routes"));
defer app.deinit();
const response = try app.request(.GET, "/song/new", .{});
try response.expectStatus(.ok);
}
test "edit" {
var app = try jetzig.testing.app(std.testing.allocator, @import("routes"));
defer app.deinit();
const response = try app.request(.GET, "/song/example-id/edit", .{});
try response.expectStatus(.ok);
}
test "post" {
var app = try jetzig.testing.app(std.testing.allocator, @import("routes"));
defer app.deinit();
const response = try app.request(.POST, "/song", .{});
try response.expectStatus(.created);
}
test "put" {
var app = try jetzig.testing.app(std.testing.allocator, @import("routes"));
defer app.deinit();
const response = try app.request(.PUT, "/song/example-id", .{});
try response.expectStatus(.ok);
}
test "patch" {
var app = try jetzig.testing.app(std.testing.allocator, @import("routes"));
defer app.deinit();
const response = try app.request(.PATCH, "/song/example-id", .{});
try response.expectStatus(.ok);
}
test "delete" {
var app = try jetzig.testing.app(std.testing.allocator, @import("routes"));
defer app.deinit();
const response = try app.request(.DELETE, "/song/example-id", .{});
try response.expectStatus(.ok);
}

View file

@ -1,3 +0,0 @@
<div>
<span>Content goes here</span>
</div>

View file

@ -1,3 +0,0 @@
<div>
<span>Content goes here</span>
</div>

View file

@ -1,3 +0,0 @@
<div>
<span>Content goes here</span>
</div>

View file

@ -1,3 +0,0 @@
<div>
<span>Content goes here</span>
</div>

View file

@ -1,3 +0,0 @@
<div>
<span>Content goes here</span>
</div>

View file

@ -1,3 +0,0 @@
<div>
<span>Content goes here</span>
</div>

View file

@ -10,12 +10,6 @@ pub fn index(request: *jetzig.Request, data: *jetzig.Data) !jetzig.View {
return request.render(.ok);
}
pub fn get(id: []const u8, request: *jetzig.Request, data: *jetzig.Data) !jetzig.View {
_ = data;
_ = id;
return request.render(.ok);
}
pub fn post(request: *jetzig.Request) !jetzig.View {
var root = try request.data(.object);
@ -166,21 +160,3 @@ pub fn post(request: *jetzig.Request) !jetzig.View {
return request.render(.created);
}
pub fn put(id: []const u8, request: *jetzig.Request, data: *jetzig.Data) !jetzig.View {
_ = data;
_ = id;
return request.render(.ok);
}
pub fn patch(id: []const u8, request: *jetzig.Request, data: *jetzig.Data) !jetzig.View {
_ = data;
_ = id;
return request.render(.ok);
}
pub fn delete(id: []const u8, request: *jetzig.Request, data: *jetzig.Data) !jetzig.View {
_ = data;
_ = id;
return request.render(.ok);
}

View file

@ -1,3 +0,0 @@
<div>
<span>Content goes here</span>
</div>

View file

@ -1,3 +0,0 @@
<div>
<span>Content goes here</span>
</div>

View file

@ -1,3 +0,0 @@
<div>
<span>Content goes here</span>
</div>

View file

@ -1,3 +0,0 @@
<div>
<span>Content goes here</span>
</div>

View file

@ -61,3 +61,14 @@ pub const Rule = struct {
pub const Rules = struct {
rules: []const Rule,
};
// Can't import types in .zmpl files, so defining this here
// doesn't really do much (except maybe in the .zig file for views?)
//pub const HeaderTypes = []enum {
// song,
// album,
// artist,
// artistlist,
// scrobbles,
// date,
//};