Skip to content

Commit

Permalink
feat(song): genre
Browse files Browse the repository at this point in the history
  • Loading branch information
Topvennie committed Nov 27, 2024
1 parent f54fa51 commit 6415eba
Show file tree
Hide file tree
Showing 14 changed files with 536 additions and 122 deletions.
4 changes: 3 additions & 1 deletion .env.example
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
app.env = development
APP_ENV = development
SONG_SPOTIFY_CLIENT_ID = your_client_id
SONG_SPOTIFY_CLIENT_SECRET = your_client_secret
11 changes: 7 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,12 @@ Displays the cammie chat along with some other statistics.

### Configuration

1. Create a `.env` file specifying the environment. Available options are:
- `development`
- `production`
1. Create a `.env` file specifying
- `APP_ENV`. Available options are:
- `development`
- `production`
- `SONG_SPOTIFY_CLIENT_ID`
- `SONG_SPOTIFY_CLIENT_SECRET`
2. Configure the appropriate settings in the corresponding configuration file located in the [config directory](./config)

## DB
Expand All @@ -26,7 +29,7 @@ SQLC is used to generate statically typed queries and goose is responsible for t

### Usefull commands

- `make migrate`: Run database migrations to update your database schema.
- `make migrate`: Run database migrations to update your database schema (watch out, migrations might result in minor data loss).
- `make create-migration`: Create a new migration in the [db/migrations](./db/migrations/) directory.
- `make sqlc`: Generate statically typed queries based on the .sql files in the [db/queries](./db/queries/) directory. Add new queries to this directory as needed.

Expand Down
2 changes: 0 additions & 2 deletions config/development.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@ host = "localhost"
port = 3000

[song]
spotify_client_id = "your_client_id"
spotify_client_secret = "your_client_secret"
spotify_api = "https://api.spotify.com/v1"
spotify_account = "https://accounts.spotify.com/api/token"

Expand Down
2 changes: 1 addition & 1 deletion db/migrations/20241127162048_add_song_history_table.sql
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ DROP COLUMN created_at;

ALTER TABLE spotify RENAME TO song;

CREATE TABLE song_history (
CREATE TABLE IF NOT EXISTS song_history (
id INTEGER PRIMARY KEY,
song_id INTEGER NOT NULL,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
Expand Down
45 changes: 45 additions & 0 deletions db/migrations/20241127165609_add_song_genre.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
-- +goose Up
-- +goose StatementBegin
ALTER TABLE song
DROP COLUMN artists;

CREATE TABLE IF NOT EXISTS song_genre (
id INTEGER PRIMARY KEY,
genre TEXT NOT NULL
);

CREATE TABLE IF NOT EXISTS song_artist (
id INTEGER PRIMARY KEY,
name TEXT NOT NULL,
spotify_id TEXT NOT NULL,
followers INTEGER NOT NULL,
popularity INTEGER NOT NULL
);

CREATE TABLE IF NOT EXISTS song_artist_song (
id INTEGER PRIMARY KEY,
artist_id INTEGER NOT NULL,
song_id INTEGER NOT NULL,
FOREIGN KEY(artist_id) REFERENCES artist(id),
FOREIGN KEY(song_id) REFERENCES song(id)
);

CREATE TABLE IF NOT EXISTS song_artist_genre (
id INTEGER PRIMARY KEY,
artist_id INTEGER NOT NULL,
genre_id INTEGER NOT NULL,
FOREIGN KEY(artist_id) REFERENCES artist(id),
FOREIGN KEY(genre_id) REFERENCES genre(id)
);
-- +goose StatementEnd

-- +goose Down
-- +goose StatementBegin
DROP TABLE IF EXISTS song_artist_genre;
DROP TABLE IF EXISTS song_artist_song;
DROP TABLE IF EXISTS song_artist;
DROP TABLE IF EXISTS song_genre;

ALTER TABLE song
ADD COLUMN artists TEXT NOT NULL DEFAULT 'Unknown';
-- +goose StatementEnd
61 changes: 59 additions & 2 deletions db/queries/song.sql
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,38 @@ FROM song
WHERE id = ?;

-- name: CreateSong :one
INSERT INTO song (title, artists, spotify_id, duration_ms)
INSERT INTO song (title, spotify_id, duration_ms)
VALUES (?, ?, ?)
RETURNING *;

-- name: CreateSongHistory :one
INSERT INTO song_history (song_id)
VALUES (?)
RETURNING *;

-- name: CreateSongGenre :one
INSERT INTO song_genre (genre)
VALUES (?)
RETURNING *;

-- name: CreateSongArtist :one
INSERT INTO song_artist (name, spotify_id, followers, popularity)
VALUES (?, ?, ?, ?)
RETURNING *;

-- name: CreateSongArtistSong :one
INSERT INTO song_artist_song (artist_id, song_id)
VALUES (?, ?)
RETURNING *;

-- name: CreateSongArtistGenre :one
INSERT INTO song_artist_genre (artist_id, genre_id)
VALUES (?, ?)
RETURNING *;

-- name: UpdateSong :one
UPDATE song
SET title = ?, artists = ?, spotify_id = ?, duration_ms = ?
SET title = ?, spotify_id = ?, duration_ms = ?
WHERE id = ?
RETURNING *;

Expand All @@ -31,3 +56,35 @@ WHERE id = ?;
SELECT *
FROM song
WHERE spotify_id = ?;

-- name: GetSongArtistBySpotifyID :one
SELECT *
FROM song_artist
WHERE spotify_id = ?;

-- name: GetLastSongHistory :one
SELECT *
FROM song_history
ORDER BY created_at DESC
LIMIT 1;

-- name: GetSongGenreByName :one
SELECT *
FROM song_genre
WHERE genre = ?;

-- name: GetSongArtistByName :one
SELECT *
FROM song_artist
WHERE name = ?;

-- name: GetLastSongFull :many
SELECT s.title AS song_title, s.spotify_id, s.duration_ms, a.name AS artist_name, g.genre AS genre
FROM song_history sh
JOIN song s ON sh.song_id = s.id
LEFT JOIN song_artist_song sa ON s.id = sa.song_id
LEFT JOIN song_artist a ON sa.artist_id = a.id
LEFT JOIN song_artist_genre ag ON ag.artist_id = a.id
LEFT JOIN song_genre g ON ag.genre_id = g.id
WHERE sh.created_at = (SELECT MAX(created_at) FROM song_history)
ORDER BY a.name, g.genre;
16 changes: 0 additions & 16 deletions db/queries/song_history.sql

This file was deleted.

67 changes: 56 additions & 11 deletions internal/pkg/db/dto/song.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,32 +4,77 @@ import (
"github.com/zeusWPI/scc/internal/pkg/db/sqlc"
)

// Song is the DTO for the song
// Song is the DTO for a song
type Song struct {
ID int64 `json:"id"`
Title string `json:"title"`
Artists string `json:"artists"`
SpotifyID string `json:"spotify_id" validate:"required"`
DurationMS int64 `json:"duration_ms"`
ID int64 `json:"id"`
Title string `json:"title"`
SpotifyID string `json:"spotify_id" validate:"required"`
DurationMS int64 `json:"duration_ms"`
Artists []SongArtist `json:"artists"`
}

// SongArtist is the DTO for a song artist
type SongArtist struct {
ID int64 `json:"id"`
Name string `json:"name"`
SpotifyID string `json:"spotify_id"`
Followers int64 `json:"followers"`
Popularity int64 `json:"popularity"`
Genres []SongGenre `json:"genres"`
}

// SongGenre is the DTO for a song genre
type SongGenre struct {
ID int64 `json:"id"`
Genre string `json:"genre"`
}

// SongDTO converts a sqlc.Song to a Song
func SongDTO(song sqlc.Song) *Song {
return &Song{
ID: song.ID,
Title: song.Title,
Artists: song.Artists,
SpotifyID: song.SpotifyID,
DurationMS: song.DurationMs,
}
}

// CreateParams converts a Song to sqlc.CreateSongParams
func (s *Song) CreateParams() sqlc.CreateSongParams {
return sqlc.CreateSongParams{
// CreateSongParams converts a Song DTO to a sqlc CreateSongParams object
func (s *Song) CreateSongParams() *sqlc.CreateSongParams {
return &sqlc.CreateSongParams{
Title: s.Title,
Artists: s.Artists,
SpotifyID: s.SpotifyID,
DurationMs: s.DurationMS,
}
}

// CreateSongGenreParams converts a Song DTO to a string to create a new genre
func (s *Song) CreateSongGenreParams(idxArtist, idxGenre int) string {
return s.Artists[idxArtist].Genres[idxGenre].Genre
}

// CreateSongArtistParams converts a Song DTO to a sqlc CreateSongArtistParams object
func (s *Song) CreateSongArtistParams(idxArtist int) *sqlc.CreateSongArtistParams {
return &sqlc.CreateSongArtistParams{
Name: s.Artists[idxArtist].Name,
SpotifyID: s.Artists[idxArtist].SpotifyID,
Followers: s.Artists[idxArtist].Followers,
Popularity: s.Artists[idxArtist].Popularity,
}
}

// CreateSongArtistSongParams converts a Song DTO to a sqlc CreateSongArtistSongParams object
func (s *Song) CreateSongArtistSongParams(idxArtist int) *sqlc.CreateSongArtistSongParams {
return &sqlc.CreateSongArtistSongParams{
ArtistID: s.Artists[idxArtist].ID,
SongID: s.ID,
}
}

// CreateSongArtistGenreParamas converts a Song DTO to a sqlc CreateSongArtistGenreParams object
func (s *Song) CreateSongArtistGenreParamas(idxArtist, idxGenre int) *sqlc.CreateSongArtistGenreParams {
return &sqlc.CreateSongArtistGenreParams{
ArtistID: s.Artists[idxArtist].ID,
GenreID: s.Artists[idxArtist].Genres[idxGenre].ID,
}
}
26 changes: 25 additions & 1 deletion internal/pkg/db/sqlc/models.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit 6415eba

Please sign in to comment.